pax_global_header00006660000000000000000000000064147774650700014533gustar00rootroot0000000000000052 comment=16815d52de7c4c6538819fa97737b88ae1a691d3 bpftrace-0.23.2/000077500000000000000000000000001477746507000134055ustar00rootroot00000000000000bpftrace-0.23.2/.clang-format000066400000000000000000000052021477746507000157570ustar00rootroot00000000000000--- Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackArguments: false BinPackParameters: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BraceWrapping: AfterFunction: true BreakAfterAttributes: Leave BreakBeforeInheritanceComma: false BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeColon BreakStringLiterals: true ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: false DerivePointerAlignment: true DisableFormat: false ForEachMacros: ['bpf_map__for_each', 'bpf_object__for_each_program'] FixNamespaceComments: true IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: true IndentPPDirectives: None IndentWidth: 2 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None # Note: These penalties are not only relative to each other, but also to # penalties for more cases hardcoded into clang-format PenaltyBreakAssignment: 100 PenaltyBreakBeforeFirstCallParameter: 75 PenaltyBreakComment: 1 PenaltyBreakFirstLessLess: 50 PenaltyBreakString: 500 PenaltyExcessCharacter: 1000 PenaltyReturnTypeOnItsOwnLine: 1000000 PointerAlignment: Right ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 8 UseTab: Never --- Language: Json DisableFormat: true ... bpftrace-0.23.2/.clang-tidy000066400000000000000000000010361477746507000154410ustar00rootroot00000000000000Checks: | -* bugprone-suspicious-memset-usage bugprone-unchecked-optional-access bugprone-undefined-memory-manipulation bugprone-unused-raii bugprone-use-after-move google-readability-casting modernize-concat-nested-namespaces modernize-deprecated-headers modernize-loop-convert modernize-make-shared modernize-make-unique modernize-raw-string-literal modernize-use-nullptr modernize-use-override modernize-redundant-void-arg performance-unnecessary-copy-initialization WarningsAsErrors: "*" UseColor: true bpftrace-0.23.2/.editorconfig000066400000000000000000000003231477746507000160600ustar00rootroot00000000000000[*] end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 indent_style = space indent_size = 2 [*.py] indent_size = 4 [tools/*.bt] indent_style = tab indent_size = unset bpftrace-0.23.2/.git-blame-ignore-revs000066400000000000000000000001221477746507000175000ustar00rootroot00000000000000# Move opening brace to starting context 453198bdabdaffdc1cb600038af60644e1326656 bpftrace-0.23.2/.gitattributes000066400000000000000000000001171477746507000162770ustar00rootroot00000000000000*.bt linguist-language=D *.bt linguist-vendored *.ll linguist-detectable=false bpftrace-0.23.2/.github/000077500000000000000000000000001477746507000147455ustar00rootroot00000000000000bpftrace-0.23.2/.github/CODEOWNERS000066400000000000000000000001531477746507000163370ustar00rootroot00000000000000# Request reviews automatically to speed up the PR workflow * @ajor @viktormalik @danobi @fbs @jordalgo bpftrace-0.23.2/.github/FUNDING.yml000066400000000000000000000000321477746507000165550ustar00rootroot00000000000000open_collective: bpftrace bpftrace-0.23.2/.github/ISSUE_TEMPLATE/000077500000000000000000000000001477746507000171305ustar00rootroot00000000000000bpftrace-0.23.2/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000005271477746507000216260ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve labels: bug --- ### What reproduces the bug? Provide code if possible. ### `bpftrace --info` output bpftrace-0.23.2/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000002601477746507000211160ustar00rootroot00000000000000blank_issues_enabled: true contact_links: - name: Community Support url: https://github.com/bpftrace/bpftrace/discussions about: Please ask and answer questions here bpftrace-0.23.2/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000004111477746507000226510ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project labels: enhancement --- ### Is your feature request related to a problem? Please describe. ### Describe the solution you'd like ### Describe alternative solutions or features you've considered bpftrace-0.23.2/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000010451477746507000205460ustar00rootroot00000000000000 ##### Checklist - [ ] Language changes are updated in `man/adoc/bpftrace.adoc` - [ ] User-visible and non-trivial changes updated in `CHANGELOG.md` - [ ] The new behaviour is covered by tests bpftrace-0.23.2/.github/actions/000077500000000000000000000000001477746507000164055ustar00rootroot00000000000000bpftrace-0.23.2/.github/actions/configure_kvm/000077500000000000000000000000001477746507000212435ustar00rootroot00000000000000bpftrace-0.23.2/.github/actions/configure_kvm/action.yml000066400000000000000000000012431477746507000232430ustar00rootroot00000000000000name: Configure KVM Permissions description: Configure KVM permissions if KVM is available on host runs: using: "composite" steps: - name: Configure KVM group perms run: | # Only configure kvm perms if kvm is available if [[ -e /dev/kvm ]]; then echo "Updating KVM permissions" echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm else echo "KVM is not available on this system, skipping permission configuration" fi shell: bash bpftrace-0.23.2/.github/codeql/000077500000000000000000000000001477746507000162145ustar00rootroot00000000000000bpftrace-0.23.2/.github/codeql/codeql-config.yml000066400000000000000000000000651477746507000214520ustar00rootroot00000000000000paths-ignore: - tests/runtime/engine/cmake_vars.py bpftrace-0.23.2/.github/include/000077500000000000000000000000001477746507000163705ustar00rootroot00000000000000bpftrace-0.23.2/.github/include/aot_allow.txt000066400000000000000000000145521477746507000211210ustar00rootroot00000000000000aot.basic.basic while loop aot.basic.clear count-map aot.basic.clear map aot.basic.delete count-map aot.basic.delete deprecated aot.basic.delete map aot.basic.exit code aot.basic.has_key exists aot.basic.has_key map keys and values aot.basic.has_key map value as key aot.basic.has_key no_exists aot.basic.increment/decrement map aot.basic.increment/decrement variable aot.basic.map strings are memset aot.basic.print large int aot.basic.variable strings are memset aot.btf.enum_value_reference aot.btf.kernel_module_tracepoint aot.btf.redefine_btf_type_missing_def aot.btf.tracepoint_nested_pointer_type_resolution aot.btf.tracepoint_pointer_type_resolution aot.btf.user_supplied_c_def_using_btf aot.builtin.arg aot.builtin.begin probe aot.builtin.cat aot.builtin.cat "no such file" aot.builtin.cat format str aot.builtin.cgroup aot.builtin.cpu aot.builtin.elapsed aot.builtin.func_kprobe aot.builtin.func_kretprobe aot.builtin.func_uprobe aot.builtin.func_uretprobe aot.builtin.gid aot.builtin.jiffies aot.builtin.kstack aot.builtin.nsecs aot.builtin.numaid aot.builtin.offsetof aot.builtin.pid aot.builtin.probe aot.builtin.rand aot.builtin.retval aot.builtin.sizeof_btf aot.builtin.sizeof_ints aot.builtin.sizeof_ints_pt2 aot.builtin.tid aot.builtin.uid aot.builtin.username aot.builtin.ustack aot.call.avg aot.call.buf_hist_map_key aot.call.buf_map_key aot.call.buf_map_value aot.call.buf_no_ascii aot.call.buf_no_ascii_no_escaping aot.call.cat aot.call.cat_more_args aot.call.clear on scalar map prevents printing aot.call.count aot.call.fmt_str_args_scratch_buf aot.call.hist aot.call.hist_2_values aot.call.hist_map_key_scratch_buf aot.call.hist_scalar_map_key_scratch_buf aot.call.kstack aot.call.kstack len aot.call.kstack perf mode aot.call.ksym aot.call.lhist aot.call.lhist_map_key_scratch_buf aot.call.lhist_scalar_map_key_scratch_buf aot.call.map len aot.call.map len keyless aot.call.map lencmp aot.call.map_val_scratch_buf aot.call.max aot.call.min aot.call.nsecs aot.call.nsecs_boot aot.call.nsecs_monotonic aot.call.nsecs_sw_tai aot.call.ntop static ip aot.call.percpu_kaddr aot.call.percpu_kaddr this cpu aot.call.print_avg_map_args aot.call.print_avg_map_with_large_top aot.call.print_hist_with_large_top_arg aot.call.print_hist_with_top_arg aot.call.print_map_item aot.call.print_non_map aot.call.printf aot.call.printf_argument aot.call.printf_char aot.call.printf_length_modifiers aot.call.printf_llu aot.call.printf_long_fmt aot.call.printf_more_arguments aot.call.pton ipv4 aot.call.pton ipv6 aot.call.scalar_map_scratch_buf aot.call.stats aot.call.str aot.call.str_big aot.call.str_big_print aot.call.str_big_read aot.call.str_big_strncmp aot.call.str_scratch_buf aot.call.sum aot.call.time aot.call.time_short aot.call.ustack aot.call.ustack len aot.call.ustack_stack_mode_env_bpftrace aot.call.ustack_stack_mode_env_override aot.call.ustack_stack_mode_env_perf aot.call.ustack_stack_mode_env_raw aot.call.usym aot.call.variable_probe_subprog_scratch_buf aot.call.variable_scratch_buf aot.for.map create map in body aot.for.map nested count aot.for.map one key elements aot.for.map stack key with a per cpu aggregation aot.for.variable context read only aot.for.variable context string aot.for.variable context update aot.other.avg can be cleared aot.other.exit exits immediately aot.other.hist can be cleared aot.other.if_gt aot.other.if_lt aot.other.ifelse_go_else aot.other.ifelse_go_if aot.other.ifelseif_go_elseif aot.other.ifelseifelse_go_else aot.other.lhist can be cleared aot.other.per_cpu_map_count_if aot.other.per_cpu_map_max_if aot.other.per_cpu_map_min_if aot.other.stats can be cleared aot.other.ternary aot.other.ternary_lnot aot.other.ternary_none_type aot.other.unroll aot.other.unroll_max_value aot.other.unroll_min_value aot.other.unroll_printf aot.precedence.operator_precedence_1 aot.precedence.operator_precedence_2 aot.precedence.operator_precedence_3 aot.precedence.operator_precedence_3 aot.probe.BEGIN aot.probe.BEGIN,END aot.probe.END_processing_after_exit aot.probe.bpf_programs_limit aot.probe.interval aot.probe.interval_probe_builtin aot.probe.interval_short_name aot.probe.kprobe aot.probe.kprobe_disallow_rcu_functions aot.probe.kprobe_func_missing aot.probe.kprobe_module aot.probe.kprobe_module_missing aot.probe.kprobe_module_wildcard aot.probe.kprobe_offset aot.probe.kprobe_short_name aot.probe.kprobe_warn_missing_probes aot.probe.kprobe_wildcard aot.probe.kretprobe aot.probe.kretprobe_short_name aot.probe.kretprobe_wildcard aot.probe.probe_builtin_scratch_buf aot.probe.profile aot.probe.profile_probe_builtin aot.probe.profile_short_name aot.probe.rawtracepoint aot.probe.rawtracepoint_missing aot.probe.rawtracepoint_short_name aot.probe.rawtracepoint_wildcard aot.probe.sanitise probe name aot.probe.software aot.probe.software_alias_probe_builtin aot.probe.software_probe_builtin aot.probe.tracepoint aot.probe.tracepoint args aot.probe.tracepoint args as pointer aot.probe.tracepoint_data_loc aot.probe.tracepoint_missing aot.probe.tracepoint_multiattach_missing aot.probe.tracepoint_short_name aot.probe.uprobe aot.probe.uprobe_address_fail_resolve aot.probe.uprobe_offset aot.probe.uprobe_offset aot.probe.uprobe_offset_fail_size aot.probe.uprobe_warn_missing_probes aot.probe.uprobe_zero_size aot.probe.uretprobe aot.regression.access ctx struct field twice aot.regression.async_id_invalid_probe_expansion aot.regression.modulo_operator aot.regression.string compare with empty aot.regression.string compare with prefix aot.regression.strncmp with prefix aot.regression.strncmp_checks_for_nul aot.regression.strncmp_n_longer_than_buffer aot.regression.unaligned key with aligned value aot.signed_ints.avg cast negative values aot.signed_ints.avg with negative values aot.signed_ints.negative map value aot.signed_ints.stats with negative values aot.signed_ints.sum negative maps aot.signed_ints.sum with negative value aot.tuples.basic tuple aot.uprobe.uprobes - probe function in non-executable library aot.variable.32-bit tracepoint arg aot.variable.buf_equality aot.variable.global_associative_arrays aot.variable.global_buf aot.variable.global_int aot.variable.global_string aot.variable.local_buf aot.variable.local_int aot.variable.local_string aot.variable.map key string type resize aot.variable.map string type resize aot.variable.scratch aot.variable.variable declaration aot.variable.variable declaration not initialized aot.variable.variable declaration with unresolved type aot.variable.variable string type resize bpftrace-0.23.2/.github/include/ci.py000077500000000000000000000227141477746507000173460ustar00rootroot00000000000000#!/usr/bin/env -S python3 -u """ This script is the entrypoint for the CI. To make CI errors easier to reproduce locally, please limit this script to using only the standard library on a recent-ish python 3 release. Please also be conservative with what tools you expect on the host system when subprocessing out. Safe things to expect are `git` and `nix`. Note that when running subprocessing _inside_ the nix env you are free to use whatever the flake provides. """ from collections import namedtuple from enum import Enum from functools import lru_cache from io import StringIO import multiprocessing import os from pathlib import Path import shutil import subprocess import sys from typing import Callable, Dict, List, Optional, Union BUILD_DIR = "build-ci" # # Knobs CI might use. We choose to use env vars b/c it's less # messy than propagating flags everywhere. # # Default nix target is empty string which by convention is the # latest LLVM release we support NIX_TARGET = os.environ.get("NIX_TARGET", "") CMAKE_BUILD_TYPE = os.environ.get("CMAKE_BUILD_TYPE", "Release") RUN_TESTS = os.environ.get("RUN_TESTS", "1") RUN_AOT_TESTS = os.environ.get("RUN_AOT_TESTS", "0") CC = os.environ.get("CC", "cc") CXX = os.environ.get("CXX", "c++") CI = os.environ.get("CI", "false") NIX_TARGET_KERNEL = os.environ.get("NIX_TARGET_KERNEL", "") TOOLS_TEST_OLDVERSION = os.environ.get("TOOLS_TEST_OLDVERSION", "") TOOLS_TEST_DISABLE = os.environ.get("TOOLS_TEST_DISABLE", "") AOT_ALLOWLIST_FILE = os.environ.get("AOT_ALLOWLIST_FILE", "") class TestStatus(Enum): PASSED = "passed" FAILED = "failed" SKIPPED = "skipped" TestResult = namedtuple("TestResult", ["test_name", "status"]) def truthy(value: str) -> bool: v = value.strip().lower() return v == "true" or v == "1" @lru_cache(maxsize=1) def root() -> Path: """Return the absolute path root of git repo""" output = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) return Path(output.decode("utf-8").strip()) def _which(cmd: str) -> Path: p = shutil.which(cmd) if not p: raise RuntimeError(f"Failed to find binary: {cmd}") return Path(p) @lru_cache(maxsize=1) def nix() -> Path: """Return the absolute path of nix binary in host env""" return _which("nix") @lru_cache(maxsize=1) def sudo() -> Path: """Return the absolute path of sudo binary in host env""" return _which("sudo") def shell( cmd: List[str], as_root: bool = False, cwd: Optional[Path] = None, env: Optional[Dict[str, str]] = None, ): """ Runs the specified command in the proper nix development environment. Note that output is sent to our inherited stderr/stdout and that any errors immediately raise an exception. """ c: List[Union[str, Path]] = [ nix(), "develop", ] if NIX_TARGET: c.append(NIX_TARGET) c.append("--command") if as_root: to_preserve = ",".join([n for n in env]) if env else [] c += [ sudo(), # We need to preserve path so that default root PATH is not # generated. If that occurs, then commands run in nix env # can escape and use host system binaries. This creates some # very hard to debug errors in CI. # # And yes, I realize that we should probably be using nix's # sandboxing via checkPhase, but unfortunately that does not # play nice with root or writing temporary files. So that # requires further investigation. "--preserve-env=PATH", "--preserve-env=PYTHONPATH", # Also preserve any caller specified env vars f"--preserve-env={to_preserve}", ] c += cmd if not env: env = {} # Nix needs to know the home dir if "HOME" in os.environ: env["HOME"] = os.environ["HOME"] subprocess.run( c, cwd=cwd if cwd else root(), check=True, # Explicitly clear the environment so that any commands run # inside the nix environment cannot accidentally depend on # host environment. There are known very-hard-to-debug issues # that occur in CI when the envirionment escapes. env=env, ) def configure(): """Run cmake configure step""" # fmt: off c = [ "cmake", "-B", BUILD_DIR, # Dynamic configs f"-DCMAKE_C_COMPILER={CC}", f"-DCMAKE_CXX_COMPILER={CXX}", f"-DCMAKE_BUILD_TYPE={CMAKE_BUILD_TYPE}", # Static configs f"-DCMAKE_VERBOSE_MAKEFILE=1", f"-DBUILD_TESTING=1", f"-DENABLE_SKB_OUTPUT=1", f"-DBUILD_ASAN=1", ] # fmt: on shell(c) def build(): """Build everything""" cpus = multiprocessing.cpu_count() shell(["make", "-C", BUILD_DIR, "-j", str(cpus)]) def test_one(name: str, cond: Callable[[], bool], fn: Callable[[], None]) -> TestResult: """Runs a single test suite and returns the result""" status = TestStatus.PASSED if cond(): print(f"\n======= {name} ======") try: fn() except subprocess.CalledProcessError as e: status = TestStatus.FAILED else: status = TestStatus.SKIPPED return TestResult(test_name=name, status=status) def tests_finish(results: List[TestResult]): """Process test results and output status""" skipped = sum(1 for r in results if r.status == TestStatus.SKIPPED) passed = sum(1 for r in results if r.status == TestStatus.PASSED) failed = sum(1 for r in results if r.status == TestStatus.FAILED) failed_names = [r.test_name for r in results if r.status == TestStatus.FAILED] total_run = passed + failed output = StringIO() print("\n======= Results =======", file=output) if skipped: print(f"{skipped} suite(s) skipped", file=output) if failed: print(f"{failed}/{total_run} suites(s) failed: {failed_names}", file=output) else: print(f"{passed}/{total_run} suites(s) passed", file=output) print("=======================", file=output) if failed: raise RuntimeError(output.getvalue()) else: print(output.getvalue()) def run_runtime_tests(): """Runs runtime tests, under a controlled kernel if requested""" script = "./tests/runtime-tests.sh" if NIX_TARGET_KERNEL: # Bring kernel into nix store then grab the path subprocess.run([nix(), "build", NIX_TARGET_KERNEL], check=True) eval = subprocess.run( [nix(), "eval", "--raw", NIX_TARGET_KERNEL], check=True, capture_output=True, text=True, ) # nf_tables and xfs are necessary for testing kernel modules BTF support modules = ["kvm", "nf_tables", "xfs"] modprobe = f"modprobe -d {eval.stdout} -a {' '.join(modules)}" c = f"{modprobe} && {script}" cmd = ["vmtest", "-k", f"{eval.stdout}/bzImage", c] else: cmd = [script] shell( cmd, # Don't need root if running tests in a VM as_root=not NIX_TARGET_KERNEL, cwd=Path(BUILD_DIR), env={ "CI": CI, "RUNTIME_TEST_COLOR": "yes", # Disable UI to make CI and manual runs behave identically "VMTEST_NO_UI": "1", }, ) def test(): """ Run all requested tests Note we're not using `ctest` b/c it's kinda a pain to work with. We don't use any of it's advanced features but still suffer from it's limitations, like not being able to flexibly configure test runners (we need `sudo` for some suites). It also buffers output rather oddly. """ results = [] results.append( test_one( "bpftrace_test", lambda: truthy(RUN_TESTS), lambda: shell( ["./tests/bpftrace_test"], cwd=Path(BUILD_DIR), env={"GTEST_COLOR": "yes"}, ), ) ) results.append( test_one( "runtime-tests.sh", lambda: truthy(RUN_TESTS), run_runtime_tests, ) ) results.append( test_one( "tools-parsing-test.sh", lambda: truthy(RUN_TESTS), lambda: shell( [ "./tests/tools-parsing-test.sh", ], as_root=True, cwd=Path(BUILD_DIR), env={ "TOOLS_TEST_OLDVERSION": TOOLS_TEST_OLDVERSION, "TOOLS_TEST_DISABLE": TOOLS_TEST_DISABLE, }, ), ) ) results.append( test_one( "runtime-tests.sh (AOT)", lambda: truthy(RUN_AOT_TESTS), lambda: shell( ( [ "./tests/runtime-tests.sh", "--run-aot-tests", "--filter", "aot.*", ] + [ "--allowlist_file", f"{root()}/{AOT_ALLOWLIST_FILE}", ] if AOT_ALLOWLIST_FILE else [] ), as_root=True, cwd=Path(BUILD_DIR), env={ "CI": CI, "RUNTIME_TEST_COLOR": "yes", }, ), ) ) tests_finish(results) def main(): configure() build() test() if __name__ == "__main__": main() bpftrace-0.23.2/.github/include/static.sh000077500000000000000000000017051477746507000202210ustar00rootroot00000000000000#!/bin/bash # # This script is the entrypoint for the static build. # # To make CI errors easier to reproduce locally, please limit # this script to using only git, docker, and coreutils. set -eux IMAGE=bpftrace-static cd $(git rev-parse --show-toplevel) # Build the base image docker build -t "$IMAGE" -f docker/Dockerfile.static docker/ # Perform bpftrace static build docker run -v $(pwd):$(pwd) -w $(pwd) -i "$IMAGE" <<'EOF' set -eux BUILD_DIR=build-static cmake -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTING=OFF -DSTATIC_LINKING=ON make -C "$BUILD_DIR" -j$(nproc) # Basic smoke test ./"$BUILD_DIR"/src/bpftrace --help # Validate that it's a mostly static binary except for libc EXPECTED="/lib/ld-musl-x86_64.so.1\nlibc.musl-x86_64.so.1" GOT=$(ldd "$BUILD_DIR"/src/bpftrace | awk '{print $1}') if ! diff <(echo -e "$EXPECTED") <(echo "$GOT"); then set +x >&2 echo "bpftrace incorrectly linked" exit 1 fi EOF bpftrace-0.23.2/.github/workflows/000077500000000000000000000000001477746507000170025ustar00rootroot00000000000000bpftrace-0.23.2/.github/workflows/binary.yml000066400000000000000000000016401477746507000210120ustar00rootroot00000000000000# This workflow builds and uploads the bpftrace appimage as a build artifact. # # This is useful for users who want to download the latest and greatest bpftrace # binary without going through a local build. name: Binary on: push: branches: - master jobs: build-and-upload: strategy: matrix: runner: [ubuntu-latest, ubuntu-24.04-arm] runs-on: ${{ matrix.runner }} # For flakehub cache permissions: id-token: write contents: read steps: - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@v16 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v1 - name: Build appimage run: | nix build .#appimage cp ./result bpftrace - name: Upload appimage uses: actions/upload-artifact@v4 with: name: bpftrace-${{ runner.arch }} path: ./bpftrace bpftrace-0.23.2/.github/workflows/ci.yml000066400000000000000000000061651477746507000201300ustar00rootroot00000000000000name: CI on: [push, pull_request] # Cancel previous run if a new one is started concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: clang-format: runs-on: ubuntu-latest # For flakehub cache permissions: id-token: write contents: read steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - uses: DeterminateSystems/nix-installer-action@v16 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v1 - name: clang-format run: nix develop --command git clang-format --diff origin/master build_test: runs-on: ubuntu-latest # For flakehub cache permissions: id-token: write contents: read continue-on-error: true strategy: matrix: env: - NAME: LLVM 16 CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm16 - NAME: LLVM 17 CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm17 - NAME: LLVM 18 CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm18 - NAME: LLVM 19 Debug CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm19 - NAME: LLVM 20 Release CMAKE_BUILD_TYPE: Release NIX_TARGET: .#bpftrace-llvm20 - NAME: LLVM 20 Debug CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm20 - NAME: LLVM 20 Clang Debug CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm20 CC: clang CXX: clang++ - NAME: Latest kernel (LLVM 20 Debug) CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm20 NIX_TARGET_KERNEL: .#kernel-6_12 - NAME: AOT (LLVM 20 Debug) CMAKE_BUILD_TYPE: Debug NIX_TARGET: .#bpftrace-llvm20 RUN_TESTS: 0 RUN_AOT_TESTS: 1 AOT_ALLOWLIST_FILE: .github/include/aot_allow.txt steps: - uses: actions/checkout@v2 - uses: DeterminateSystems/nix-installer-action@v16 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v1 - uses: ./.github/actions/configure_kvm - name: Load kernel modules # nf_tables and xfs are necessary for testing kernel modules BTF support run: | sudo modprobe nf_tables sudo modprobe xfs - name: Build and test env: ${{matrix.env}} run: ./.github/include/ci.py irc: # Notify IRC of build failures on pushes only if we are running from # the main repo. We don't want this rule to trigger from forked repos. needs: - build_test if: "failure() && github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'bpftrace/bpftrace'" runs-on: ubuntu-latest steps: - name: Message channel uses: rectalogic/notify-irc@v1 with: nickname: bpftrace-ci-bot server: irc.oftc.net port: 6667 tls: false channel: "#bpftrace" message: | master is BROKEN at https://github.com/bpftrace/bpftrace/commit/${{github.sha}} bpftrace-0.23.2/.github/workflows/codeql.yml000066400000000000000000000033211477746507000207730ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ "master" ] pull_request: branches: [ "master" ] schedule: - cron: "19 22 * * 1" # Cancel previous run if a new one is started concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write # For flakehub cache id-token: write strategy: fail-fast: false matrix: language: [ python, cpp ] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: recursive - uses: DeterminateSystems/nix-installer-action@v16 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v1 - name: Configure (cpp) if: ${{ matrix.language == 'cpp' }} run: | mkdir $GITHUB_WORKSPACE/build && cd $GITHUB_WORKSPACE/build nix develop --command cmake .. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: config-file: ./.github/codeql/codeql-config.yml languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild uses: github/codeql-action/autobuild@v2 if: ${{ matrix.language == 'python' }} - name: Build cpp if: ${{ matrix.language == 'cpp' }} run: | cd $GITHUB_WORKSPACE/build nix develop --command make -j$(nproc) - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: category: "/language:${{ matrix.language }}" bpftrace-0.23.2/.github/workflows/distros.yml000066400000000000000000000010331477746507000212110ustar00rootroot00000000000000name: Distros on: [push] # Cancel previous run if a new one is started concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: build: runs-on: ubuntu-latest continue-on-error: true strategy: matrix: dockerfile: - docker/Dockerfile.debian - docker/Dockerfile.fedora - docker/Dockerfile.ubuntu steps: - uses: actions/checkout@v2 - name: Build run: docker build -f ${{ matrix.dockerfile }} . bpftrace-0.23.2/.github/workflows/flakehub.yml000066400000000000000000000015631477746507000213130ustar00rootroot00000000000000# This job pushes tagged releases to flakehub. name: Flakehub on: push: tags: - v?[0-9]+.[0-9]+.[0-9]+* workflow_dispatch: inputs: tag: description: The existing tag to publish to FlakeHub type: string required: true jobs: flakehub-publish: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - uses: actions/checkout@v4 with: ref: ${{ (inputs.tag != null) && format('refs/tags/{0}', inputs.tag) || '' }} - uses: DeterminateSystems/nix-installer-action@v16 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v1 - uses: DeterminateSystems/flakehub-push@v5 with: visibility: public name: bpftrace/bpftrace tag: ${{ inputs.tag }} include-output-paths: true bpftrace-0.23.2/.github/workflows/release.yml000066400000000000000000000014251477746507000211470ustar00rootroot00000000000000# This job listens for new releases and will build the appropriate artifacts # and upload them to the release. name: Release on: release: types: [published] jobs: build-upload: runs-on: ubuntu-latest permissions: # For flakehub cache id-token: write # For artifact upload contents: write steps: - uses: actions/checkout@v3 - uses: DeterminateSystems/nix-installer-action@v16 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v1 - name: Build artifacts run: nix develop --command bash -c "OUT=./assets ./scripts/create-assets.sh" - name: Upload artifacts env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: gh release upload ${{ github.ref_name }} ./assets/* bpftrace-0.23.2/.github/workflows/static.yml000066400000000000000000000005631477746507000210200ustar00rootroot00000000000000name: Static on: [push, pull_request] # Cancel previous run if a new one is started concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build static bpftrace run: ./.github/include/static.sh bpftrace-0.23.2/.github/workflows/tidy.yml000066400000000000000000000013031477746507000204730ustar00rootroot00000000000000name: "Clang Tidy" on: pull_request: branches: [ "master" ] # Cancel previous run if a new one is started concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read pull-requests: write # For flakehub cache id-token: write steps: - uses: actions/checkout@v3 - uses: DeterminateSystems/nix-installer-action@v16 with: determinate: true - uses: DeterminateSystems/flakehub-cache-action@v1 - name: Run clang-tidy run: ./scripts/clang_tidy.sh bpftrace-0.23.2/.gitignore000066400000000000000000000002671477746507000154020ustar00rootroot00000000000000/.idea build/ build-*/ tests/runtime/*.pyc .vagrant tests/**/*.bc *~ \#*\# .\#* [._]*.s[a-v][a-z] [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] [._]sw[a-p] *.btaot result ignored/ bpftrace-0.23.2/CHANGELOG.md000066400000000000000000003545371477746507000152370ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased ## [0.23.2] 2025-04-14 #### Fixed - Fix 32-bit build failures due to missing cast - [#4008](https://github.com/bpftrace/bpftrace/pull/4008) #### Tools - Fix biosnoop.bt to print comm from block_io_start probe - [#4013](https://github.com/bpftrace/bpftrace/pull/4013) ## [0.23.1] 2025-04-11 #### Fixed - Fix build failures due to missing location.hh - [#4000](https://github.com/bpftrace/bpftrace/pull/4000) ## [0.23.0] 2025-03-25 #### Breaking Changes - Remove '-kk' command line opt, surface some BPF errors by default, and make '-k' surface probe read errors - [#3784](https://github.com/bpftrace/bpftrace/pull/3784) #### Added - `offsetof()` now supports sub fields e.g. `offsetof(struct Foo, bar.a.b);` - [#3761](https://github.com/bpftrace/bpftrace/pull/3761) - Pointers may now be used in if conditions, tenary conditions and as operands in logical AND and OR expressions - [#3656](https://github.com/bpftrace/bpftrace/pull/3656) - `len` now also accepts `ustack` and `kstack` as arguments - [#3769](https://github.com/bpftrace/bpftrace/pull/3769) - `blazesym` will be used for kernel address symbolication if found during build - [#3760](https://github.com/bpftrace/bpftrace/pull/3760) - [#3787](https://github.com/bpftrace/bpftrace/pull/3787) - Published aarch64 appimage builds from master - [#3795](https://github.com/bpftrace/bpftrace/pull/3795) - Add ability to cast int to an enum - [#3812](https://github.com/bpftrace/bpftrace/pull/3812) - Added warning when strcontains() is used on strings that are too big and may cause verifier issues - [#3811](https://github.com/bpftrace/bpftrace/pull/3811) - Add support for LLVM 20 - [#3841](https://github.com/bpftrace/bpftrace/pull/3841) #### Changed - `probe` builtin is now represented as a string type - [#3638](https://github.com/bpftrace/bpftrace/pull/3638) - Change bpftrace help flag output from stderr to stdout - [#3678](https://github.com/bpftrace/bpftrace/pull/3678) - Change max_strlen default from 64 to 1024 - [#3713](https://github.com/bpftrace/bpftrace/pull/3713) - Add feature check for castable map reads - [#3752](https://github.com/bpftrace/bpftrace/pull/3752) - Increase default values for max_bpf_progs and max_probes - [#3808](https://github.com/bpftrace/bpftrace/pull/3808) - Allow use of variables before they are assigned - [#3832](https://github.com/bpftrace/bpftrace/pull/3832) #### Deprecated #### Removed - Drop support for LLVM 14 and 15 - [#3825](https://github.com/bpftrace/bpftrace/pull/3825) #### Fixed - Fix json output for none type - [#3692](https://github.com/bpftrace/bpftrace/pull/3692) - Fix bug where strftime() %f specifier could be off by up to 1s - [#3704](https://github.com/bpftrace/bpftrace/pull/3704) - Fix `pid`, `tid` and `ustack` when running bpftrace in containers with PID namespacing - [#3428](https://github.com/bpftrace/bpftrace/pull/3428) - Do not generate functions for empty attach points - [#3715](https://github.com/bpftrace/bpftrace/pull/3715) - Fix ternary expression to accept all types - [#3765](https://github.com/bpftrace/bpftrace/pull/3765) - Fix feature detection for tracing program types - [#3805](https://github.com/bpftrace/bpftrace/pull/3805) - Fix strcontains() correctness bug where matches could be lost if both strings are non-literal - [#3811](https://github.com/bpftrace/bpftrace/pull/3811) - Fix str() bug where optional size parameter did not count towards NUL terminator - [#3849](https://github.com/bpftrace/bpftrace/pull/3849) #### Security #### Docs #### Tools - Fix dcsnoop.bt on newer kernels - [#3715](https://github.com/bpftrace/bpftrace/pull/3715) ## [0.22.0] 2025-01-07 #### Breaking Changes - Return `uint32` instead of `uint64` for `pid` and `tid` builtins - [#3441](https://github.com/bpftrace/bpftrace/pull/3441) - [Migration guide](docs/migration_guide.md#pid-and-tid-builtins-return-uint32) - Remove multi-map `delete` functionality - [#3506](https://github.com/bpftrace/bpftrace/pull/3506) - [Migration guide](docs/migration_guide.md#multi-key-delete-removed) - Add lexical/block scoping for variables - [#3367](https://github.com/bpftrace/bpftrace/pull/3367) - [Migration guide](docs/migration_guide.md#added-block-scoping-for-scratch-variables) - Replace default map printing on `SIGUSR1` with custom signal handling probes - [#3522](https://github.com/bpftrace/bpftrace/pull/3522) - [Migration guide](docs/migration_guide.md#default-sigusr1-handler-removed) #### Added - Add env var to colorize bpftrace log output - [#3710](https://github.com/bpftrace/bpftrace/pull/3710) - Bump max supported LLVM version to 19 - [#3433](https://github.com/bpftrace/bpftrace/pull/3433) - Add `--dry-run` CLI option - [#3203](https://github.com/bpftrace/bpftrace/pull/3203) - Enable avg map reads in kernel space (implicit casting) - [#3268](https://github.com/bpftrace/bpftrace/pull/3268) - Enable for-loops in multiple probes - [#3285](https://github.com/bpftrace/bpftrace/pull/3285) - For-loops: Allow sharing variables between the main probe and the loop's body - [#3014](https://github.com/bpftrace/bpftrace/pull/3014) - Parse C++ Class and Inheritance from Debug Info - [#3094](https://github.com/bpftrace/bpftrace/pull/3094) - Add an optional `size` parameter to `path` - [#3401](https://github.com/bpftrace/bpftrace/pull/3401) - Allow tuples to be used as map keys - [#3390](https://github.com/bpftrace/bpftrace/pull/3390/) - Add `has_key` function for maps - [#3358](https://github.com/bpftrace/bpftrace/pull/3358) - Add ability to attach kprobes to inlined functions - [#3301](https://github.com/bpftrace/bpftrace/pull/3095) - Variable declarations with `let` - [#3461](https://github.com/bpftrace/bpftrace/pull/3461) - Support symbolizing enum values using `%s` specifier in `printf()` - [#3515](https://github.com/bpftrace/bpftrace/pull/3515) - Configuration option to suppress printing maps by default at program exit - [#3547](https://github.com/bpftrace/bpftrace/pull/3547) - Add `symbol_source` config to source uprobe locations from either DWARF or the Symbol Table - [#3504](https://github.com/bpftrace/bpftrace/pull/3504/) - Introduce builtin to access percpu kernel data - [#3596](https://github.com/bpftrace/bpftrace/pull/3596/) #### Changed - Merge output into `stdout` when `-lv` - [#3383](https://github.com/bpftrace/bpftrace/pull/3383) - Stream output when printing maps - [#3264](https://github.com/bpftrace/bpftrace/pull/3264) - Only print kernel headers not found message if parsing fails - [#3265](https://github.com/bpftrace/bpftrace/pull/3265) - Add mandatory "stage" argument to the `-d` CLI option - [#3203](https://github.com/bpftrace/bpftrace/pull/3203) - Allow simultaneous use of `-v` and `-d` - [#3203](https://github.com/bpftrace/bpftrace/pull/3203) - Remove length limitations for the `buf` builtin function - [#3249](https://github.com/bpftrace/bpftrace/pull/3249) - Change `delete` API to accept a map and key as separate args - [#3472](https://github.com/bpftrace/bpftrace/pull/3472) - Symbolize enums when used in maps - [#3539](https://github.com/bpftrace/bpftrace/pull/3539) - Supported LLVM version for static builds changed to LLVM 18 - [#3631](https://github.com/bpftrace/bpftrace/pull/3631) #### Deprecated #### Removed - Remove the `-dd` CLI option - [#3203](https://github.com/bpftrace/bpftrace/pull/3203) - Drop support for LLVM 12 and below - [#3325](https://github.com/bpftrace/bpftrace/pull/3325) - Remove `ALLOW_UNSAFE_PROBE` compiler flag - [#3476](https://github.com/bpftrace/bpftrace/pull/3476) #### Fixed - Fix verifier error when array indexing through pointer - [#3465](https://github.com/bpftrace/bpftrace/pull/3465) - Fix segfault for multi-tracepoint probes - [#3274](https://github.com/bpftrace/bpftrace/pull/3274) - Fix verifier error from misaligned stack access when using strings as map keys - [#3294](https://github.com/bpftrace/bpftrace/issues/3294) - Fix min/max map functions - [#3298](https://github.com/bpftrace/bpftrace/pull/3298) - Fix stack mode for stack builtin - [#3322](https://github.com/bpftrace/bpftrace/pull/3322) - Fix lldb support in appimage builds - #[3339](https://github.com/bpftrace/bpftrace/pull/3339) - Fix parsing large unsigned int strings as positional params - [#3336](https://github.com/bpftrace/bpftrace/pull/3336) - Fix json formatting for `strftime` function - [#3381](https://github.com/bpftrace/bpftrace/pull/3381) - Fix BTF/DWARF parsing for structs contained in arrays - [#3422](https://github.com/bpftrace/bpftrace/pull/3422) - Fix integer comparisons and auto casting for scratch variables - [#3416](https://github.com/bpftrace/bpftrace/pull/3416) - Fix tuple resizing - [#3443](https://github.com/bpftrace/bpftrace/pull/3443) - Handle invalid BTF without crashing - [#3453](https://github.com/bpftrace/bpftrace/pull/3453) - Fix json formatting for hex values - [#3475](https://github.com/bpftrace/bpftrace/pull/3475) - Fix binary operations on integers always returning 64 bit values - [#3517](https://github.com/bpftrace/bpftrace/pull/3517) - Fix verifier error when comparing result of len() - [#3308](https://github.com/bpftrace/bpftrace/issues/3308) - Fix type back propagation for map keys - [#3536](https://github.com/bpftrace/bpftrace/pull/3536) - Fix crash by adding checks for bad var/map assignments - [#3542](https://github.com/bpftrace/bpftrace/pull/3542) - Fix field access and offsetof for strings that are builtin types - [#3565](https://github.com/bpftrace/bpftrace/pull/3565) - Fix crash when using castable per-cpu map types as map keys - [#3594](https://github.com/bpftrace/bpftrace/pull/3594) - Fix loop values with per-cpu aggregations - [#3664](https://github.com/bpftrace/bpftrace/pull/3664) #### Security #### Docs - Remove mention of unsupported character literals - [#3283](https://github.com/bpftrace/bpftrace/pull/3283) #### Tools - Fix bashreadline tool probe for dynamically linked readline - [#3564](https://github.com/bpftrace/bpftrace/pull/3564) - Switch all bio* tools to tracepoints - [#3622](https://github.com/bpftrace/bpftrace/pull/3622) ## [0.21.0] 2024-06-21 #### Added - Add `lazy_symbolication` config option - [#2958](https://github.com/bpftrace/bpftrace/pull/2958) - Add ability to list all probes in a program - [#2969](https://github.com/bpftrace/bpftrace/pull/2969) - Add ability to call print() with indexed maps to print single map values - [#3027](https://github.com/bpftrace/bpftrace/pull/3027) - Add LLVM 18 support - [#3051](https://github.com/bpftrace/bpftrace/pull/3051) - Add ability to call delete() with multiple arguments - [#3046](https://github.com/bpftrace/bpftrace/pull/3046) - Add for-each loops for iterating over map elements - [#3003](https://github.com/bpftrace/bpftrace/pull/3003) - Add optional systemd support - [#3158](https://github.com/bpftrace/bpftrace/pull/3158) - Add ability to attach uprobes to inlined functions - [#3095](https://github.com/bpftrace/bpftrace/pull/3095) - Enable count, sum, min, and max map reads in kernel space (implicit casting) - [#3189](https://github.com/bpftrace/bpftrace/pull/3189) - [#3226](https://github.com/bpftrace/bpftrace/pull/3226) - Add config option for handling missing probes - [#3246](https://github.com/bpftrace/bpftrace/pull/3246) - Support large arguments for printf() and print() - [#3368](https://github.com/bpftrace/bpftrace/pull/3368) - Add ability to call exit() with an exit code - [#3501](https://github.com/bpftrace/bpftrace/pull/3501) #### Changed - Better error message for args in mixed probes - [#3047](https://github.com/bpftrace/bpftrace/pull/3047) - Reproducible Builds: Do not store timestamps in gzip header - [#3096](https://github.com/bpftrace/bpftrace/pull/3096) - Improve DWARF support, using liblldb instead of libdw - [#3042](https://github.com/bpftrace/bpftrace/pull/3042) - Use new hash function to reduce collisions when aggregating on stack traces - [#3060](https://github.com/bpftrace/bpftrace/pull/3060) - Disable func builtin for kretprobes and uretprobes when `get_func_ip` feature is not available - [#2645](https://github.com/bpftrace/bpftrace/pull/2645) - Move error printing from debug to verbose mode - [#3202](https://github.com/bpftrace/bpftrace/pull/3202) - Better error message when libbpf is too old - [#3212](https://github.com/bpftrace/bpftrace/pull/3212) - Allow trailing semicolons and empty blocks in config syntax - [#3077](https://github.com/bpftrace/bpftrace/pull/3077) - Allow attaching to `spin_lock` functions with mitigations to prevent deadlocks - [#3206](https://github.com/bpftrace/bpftrace/pull/3206) - Remove length limitations for strings coming out of `str()` and `path()` - [#3228](https://github.com/bpftrace/bpftrace/pull/3228) - [#3237](https://github.com/bpftrace/bpftrace/pull/3237) - [#3245](https://github.com/bpftrace/bpftrace/pull/3245) #### Deprecated - Deprecate `sarg` builtin - [#3095](https://github.com/bpftrace/bpftrace/pull/3095) #### Removed #### Fixed - Fix ability to interrupt bpftrace during probe attach - [#3053](https://github.com/bpftrace/bpftrace/pull/3053) - Fix field resolution on structs with anon union as first field - [#2964](https://github.com/bpftrace/bpftrace/pull/2964) - Fix alignment of atomic map counter update - [#3045](https://github.com/bpftrace/bpftrace/pull/3045) - Fix func builtin for kretprobes and uretprobes for kernels with working `get_func_ip` feature - [#2645](https://github.com/bpftrace/bpftrace/pull/2645) - Fix ustack missing the second-from-top stack frame in uprobes - [#3095](https://github.com/bpftrace/bpftrace/pull/3095) - Fix storing strings of differing lengths in a variable - [#3178](https://github.com/bpftrace/bpftrace/pull/3178) - Fix field resolution for structs in arrays - [#3024](https://github.com/bpftrace/bpftrace/pull/3024) - Fix error in dereferencing kernel double pointers - [#3024](https://github.com/bpftrace/bpftrace/pull/3024) - Fix variable corruption when used as map key - [#3195](https://github.com/bpftrace/bpftrace/pull/3195) - Fix crash when assigning a record type to a map - [#3220](https://github.com/bpftrace/bpftrace/pull/3220) - Fix type resolution for pointers with `BTF_KIND_TYPE_TAG` - [#3240](https://github.com/bpftrace/bpftrace/pull/3240) - Fix attachment of probes attaching to wildcarded and non-wildcarded kprobes - [#3246](https://github.com/bpftrace/bpftrace/pull/3246) #### Security - Don't unpack kernel headers or look in tmp - [#3156](https://github.com/bpftrace/bpftrace/pull/3156) #### Docs #### Tools - Ignore warnings for missing probes - [#3246](https://github.com/bpftrace/bpftrace/pull/3246) ## [0.20.0] 2024-01-22 #### Added - Add log2 histograms with finer granularity - [#2831](https://github.com/bpftrace/bpftrace/pull/2831) - Add a `jiffies` builtin for advanced usages - [#2769](https://github.com/bpftrace/bpftrace/pull/2769) - Emit better errors messages for invalid attachpoints - [#2781](https://github.com/bpftrace/bpftrace/pull/2781) - Add support for uprobe_multi link - [#2810](https://github.com/bpftrace/bpftrace/pull/2810) - Attach BTF to generated BPF programs - [#2804](https://github.com/bpftrace/bpftrace/pull/2804) - Add fentry/fexit aliases for kfunc/kretfunc - [#2844](https://github.com/bpftrace/bpftrace/pull/2844) - Add support for uprobe pid targeting - [#2830](https://github.com/bpftrace/bpftrace/pull/2830) - New builtin for getting the number of map elements - [#2840](https://github.com/bpftrace/bpftrace/pull/2840) - Add more helpful error messages for map operations - [#2905](https://github.com/bpftrace/bpftrace/pull/2905) - New config block syntax - [#2815](https://github.com/bpftrace/bpftrace/pull/2815) - Add support for kprobe:module:function - [#2897](https://github.com/bpftrace/bpftrace/pull/2897) #### Changed - Standardize config and env var names - [#2815](https://github.com/bpftrace/bpftrace/pull/2815) #### Deprecated #### Removed - Remove snapcraft support - [#2832](https://github.com/bpftrace/bpftrace/pull/2832) #### Fixed - Fix JSON output for cgroup_path - [#2793](https://github.com/bpftrace/bpftrace/pull/2793) - Fix silent truncation of 64-bit values in hist() - [#2822](https://github.com/bpftrace/bpftrace/pull/2822) - utils: use /data/local/tmp as temprary dir on Android - [#2828](https://github.com/bpftrace/bpftrace/pull/2828) - Fix uprobe multi probe for targets with wildcards - [#2851](https://github.com/bpftrace/bpftrace/pull/2851) - Fix symbolication on for 32-bit userspcae and 64-bit kernel - [#2869](https://github.com/bpftrace/bpftrace/pull/2869) - Fix retval for kretfunc/fexit - [#2864](https://github.com/bpftrace/bpftrace/pull/2864) - Fix attachment/listing of wildcarded module kfuncs - [#2914](https://github.com/bpftrace/bpftrace/pull/2914) - Fix uprobe attachment across container boundary - [#2662](https://github.com/bpftrace/bpftrace/pull/2662) - Fix generated BTF for older kernels - [#2934](https://github.com/bpftrace/bpftrace/pull/2934) #### Docs - Fix one-liner tutorial for systems with BTF - [#2919](https://github.com/bpftrace/bpftrace/pull/2919) - [#2924](https://github.com/bpftrace/bpftrace/pull/2924) #### Tools - Add PPID field to `execsnoop.bt` - [#2876](https://github.com/bpftrace/bpftrace/pull/2876) - Use `strftime` instead of `elapsed` in `execsnoop.bt` - [#2904](https://github.com/bpftrace/bpftrace/pull/2904) - Use `strftime` instead of `elapsed` in `threadsnoop.bt` - [#2917](https://github.com/bpftrace/bpftrace/pull/2917) - Increase PID field width and align to the right in `threadsnoop.bt` - [#2928](https://github.com/bpftrace/bpftrace/pull/2928) - Update runqlen.bt to remove `runnable_weight` field from cfs_rq struct. - [#2790](https://github.com/bpftrace/bpftrace/pull/2790) - Update mdflush.bt to use blkdev.h instead of genhd.h for non-BTF builds. - [#2849](https://github.com/bpftrace/bpftrace/pull/2849) - Add milliseconds to timestamp and align numbers to the right in `killsnoop.bt` - [#2936](https://github.com/bpftrace/bpftrace/pull/2936) ## [0.19.0] 2023-09-19 #### Added - Rawtracepoint support wildcards and list show - [#2588](https://github.com/bpftrace/bpftrace/pull/2588) - Support all iterators - [#2630](https://github.com/bpftrace/bpftrace/pull/2630) - Improve working with all probe params (kfunc, uprobe) - [#2477](https://github.com/bpftrace/bpftrace/pull/2477) - Support func builtin for k(ret)func probes - [#2692](https://github.com/bpftrace/bpftrace/pull/2692) - Support casting int <-> int array - [#2686](https://github.com/bpftrace/bpftrace/pull/2686) - Support targeting all running processes for USDTs - [#2734](https://github.com/bpftrace/bpftrace/pull/2734) - Support targeting all running processes for uprobes/uretprobes - [#2757](https://github.com/bpftrace/bpftrace/pull/2757) #### Changed - Make `args` a structure (instead of a pointer) - [#2578](https://github.com/bpftrace/bpftrace/pull/2578) - Improve user symbol resolution - [#2386](https://github.com/bpftrace/bpftrace/pull/2386) - uprobes: make C++ symbol demangling explicit - [#2657](https://github.com/bpftrace/bpftrace/pull/2657) - uprobe: improve C++ probes listing - [#2693](https://github.com/bpftrace/bpftrace/pull/2693) #### Removed - Delete embedded build support and surrounding infra - [#2742](https://github.com/bpftrace/bpftrace/pull/2742) #### Fixed - Fix resolving username for malformed /etc/passwd - [#2631](https://github.com/bpftrace/bpftrace/pull/2631) - Fix crashes when maps are concurrently modified - [#2623](https://github.com/bpftrace/bpftrace/pull/2623) - Fix alignment of byte arrays inside tuples - [#2625](https://github.com/bpftrace/bpftrace/pull/2625) - cmake: fix linking libbfd - [#2673](https://github.com/bpftrace/bpftrace/pull/2673) - Allow '+' in attach point path - [#2696](https://github.com/bpftrace/bpftrace/pull/2696) - Improve listing and 'probe' builtin for several probe types - [#2691](https://github.com/bpftrace/bpftrace/pull/2691) - Allow probe builtin with aliased software/hardware probes - [#2711](https://github.com/bpftrace/bpftrace/pull/2711) - Support executing symlinked binaries with `-c` - [#2708](https://github.com/bpftrace/bpftrace/pull/2708) - Add access to `CLOCK_MONOTONIC` with `nsecs(monotonic)` - [#2718](https://github.com/bpftrace/bpftrace/pull/2718) - iter: Skip structures with '__safe_trusted' suffix - [#2732](https://github.com/bpftrace/bpftrace/pull/2732) - Improve detection of unknown typedefs in ClangParser - [#2763](https://github.com/bpftrace/bpftrace/pull/2763) ## [0.18.0] 2023-05-15 #### Added - Add `iter:task_vma` iterators detection - [#2524](https://github.com/bpftrace/bpftrace/pull/2524) - Support parsing bitfields from BTF/DWARF - [#2505](https://github.com/bpftrace/bpftrace/pull/2505) - Support printing entire structs - [#2557](https://github.com/bpftrace/bpftrace/pull/2557) - BTF support for tracepoints defined in modules - [#2479](https://github.com/bpftrace/bpftrace/pull/2479) - Add trailer to truncated strings - [#2559](https://github.com/bpftrace/bpftrace/pull/2559) - Enable watchpoint support for PowerPC - [#2577](https://github.com/bpftrace/bpftrace/pull/2577) - Add new function, `offsetof`, get the offset of the element in the struct - [#2579](https://github.com/bpftrace/bpftrace/pull/2579) - Add 'StackMode::raw' for ustack and kstack formatting - [#2581](https://github.com/bpftrace/bpftrace/pull/2581) - Add 'BPFTRACE_STACK_MODE' env variable - [#2586](https://github.com/bpftrace/bpftrace/pull/2586) #### Changed - Improve attaching to uprobes with size 0 - [#2562](https://github.com/bpftrace/bpftrace/pull/2562) - Support ringbuf output - [#2475](https://github.com/bpftrace/bpftrace/pull/2475) #### Deprecated #### Removed #### Fixed - Simplify and fix probe index assignment - [#2482](https://github.com/bpftrace/bpftrace/pull/2482) - Handle colon in positional param used in attachpoint - [#2514](https://github.com/bpftrace/bpftrace/pull/2514) - Handle BPF load errors for multi-attachpoints - [#2521](https://github.com/bpftrace/bpftrace/pull/2521) - Respect BPFTRACE_STRLEN environment variable for all strings - [#2545](https://github.com/bpftrace/bpftrace/pull/2545) - Treat str() builtin's len parameter as int64 - [#2546](https://github.com/bpftrace/bpftrace/pull/2546) - arm64: define the KASAN_SHADOW_SCALE_SHIFT macro - [#2518](https://github.com/bpftrace/bpftrace/pull/2518) - Fix segfaults in dwarf_parser - [#2587](https://github.com/bpftrace/bpftrace/pull/2587) #### Docs #### Tools ## [0.17.0] 2023-01-30 #### Added - Support for 32-bit ARM systems - [#2360](https://github.com/bpftrace/bpftrace/pull/2360) - Support BTF for kernel modules - [#2315](https://github.com/bpftrace/bpftrace/pull/2315) - Add %rh option to print buffer as hex without \x - [#2445](https://github.com/bpftrace/bpftrace/pull/2445) - Add stdbool.h to built-in headers - [#2380](https://github.com/bpftrace/bpftrace/pull/2380) - Add `strcontains` builtin function, find a substring in a string - [#2393](https://github.com/bpftrace/bpftrace/pull/2393) #### Changed - Raise minimum versions for libbpf and bcc and vendor them for local builds - [#2369](https://github.com/bpftrace/bpftrace/pull/2369) - [#2370](https://github.com/bpftrace/bpftrace/pull/2370) - Support comparison for integer arrays - [#2387](https://github.com/bpftrace/bpftrace/pull/2387) #### Deprecated #### Removed - Drop Ubuntu 19.10 lockdown detection - [#2467](https://github.com/bpftrace/bpftrace/pull/2467) #### Fixed - Fix pointer/register loads on 32-bit architectures - [#2361](https://github.com/bpftrace/bpftrace/pull/2361) - Fix kprobe multi-attachment - [#2381](https://github.com/bpftrace/bpftrace/pull/2381) - Fix attaching to multiple USDT probes using the same wildcard - [#2456](https://github.com/bpftrace/bpftrace/pull/2456) - Fix pointer arithmetics codegen - [#2397](https://github.com/bpftrace/bpftrace/pull/2397) - Fix segfault for invalid AssignVarStatement visit - [#2423](https://github.com/bpftrace/bpftrace/pull/2423) - Better handling of missing function trace support files - [#2433](https://github.com/bpftrace/bpftrace/pull/2433) - Fix unroll ID reset - [#2439](https://github.com/bpftrace/bpftrace/pull/2439) - Support profile and interval probes in probe matcher - [#2443](https://github.com/bpftrace/bpftrace/pull/2443) - Fix BTF detection macro in tools/old/mdflush.bt - [#2444](https://github.com/bpftrace/bpftrace/pull/2444) #### Docs #### Tools ## [0.16.0] 2022-08-30 #### Added - Add builtin: `numaid` - [#2177](https://github.com/bpftrace/bpftrace/pull/2177) - Add helper verifier error handling - [#2279](https://github.com/bpftrace/bpftrace/pull/2279) - Add builtin: `pton` - [#2289](https://github.com/bpftrace/bpftrace/pull/2289) - Add builtin: `debugf` - [#2307](https://github.com/bpftrace/bpftrace/pull/2307) - Add builtin: `strerror` - [#2329](https://github.com/bpftrace/bpftrace/pull/2329) #### Changed - Move from BCC to libbpf - [#2265](https://github.com/bpftrace/bpftrace/pull/2265) - Add non-uprobe based BEGIN/END implementation - [#2264](https://github.com/bpftrace/bpftrace/pull/2264) - Helper errors (-k, -kk options) are now emitted to text or json output - [#2326](https://github.com/bpftrace/bpftrace/pull/2326) - kprobe offset verification is now optional, without requiring --unsafe - [#2332](https://github.com/bpftrace/bpftrace/pull/2332) #### Deprecated #### Removed #### Fixed - Disallow different lhist bounds in a single map - [#2204](https://github.com/bpftrace/bpftrace/pull/2204) - Serialize empty histogram as an empty JSON array - [#2250](https://github.com/bpftrace/bpftrace/pull/2250) - Handle enum values in tracepoint format defs - [#2236](https://github.com/bpftrace/bpftrace/pull/2236) - Fix compound assignments with non-unary expr - [#2291](https://github.com/bpftrace/bpftrace/pull/2293) - Fix invalid LLVM IR in join builtin - [#2296](https://github.com/bpftrace/bpftrace/pull/2296) - Fix lexer buffer size check - [#2313](https://github.com/bpftrace/bpftrace/pull/2313) - Fix invalid LLVM IR as detected by tests - [#2316](https://github.com/bpftrace/bpftrace/pull/2316) - Fix builds against libbfd(binutils) >=2.39 - [#2328](https://github.com/bpftrace/bpftrace/pull/2328) - Fix access to ctx - [#2343](https://github.com/bpftrace/bpftrace/pull/2343) #### Docs #### Tools - Add sslsnoop and ssllatency tools - [#2117](https://github.com/bpftrace/bpftrace/pull/2117) - Add undump tool. - [#2225](https://github.com/bpftrace/bpftrace/pull/2225) ## [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/bpftrace/bpftrace/issues/2228) #### Added - Add option for unconditional hex output - [#2200](https://github.com/bpftrace/bpftrace/pull/2200) - Add builtin function: `cgroup_path` - [#2055](https://github.com/bpftrace/bpftrace/pull/2055) - Limit number of generated BPF programs - [#2141](https://github.com/bpftrace/bpftrace/pull/2141) - Support the octal format specifier (`%o`) in `printf` - [#2147](https://github.com/bpftrace/bpftrace/pull/2147) - Improve include paths resolution - [#2149](https://github.com/bpftrace/bpftrace/pull/2149) - Automatic type resolution from DWARF - [#2034](https://github.com/bpftrace/bpftrace/pull/2034) - Add builtin function: `bswap` - [#2166](https://github.com/bpftrace/bpftrace/pull/2166) - Print all maps to stdout on `SIGUSR1` - [#2203](https://github.com/bpftrace/bpftrace/pull/2203) - Add builtin function: `skb_output` - [#2223](https://github.com/bpftrace/bpftrace/pull/2223) #### Changed - Use auto-resolution of library paths for tools - [#2181](https://github.com/bpftrace/bpftrace/pull/2181) - Improve handling empty attach points - [#2179](https://github.com/bpftrace/bpftrace/pull/2179) #### Fixed - Fix precedence of multiplicative operations - [#2096](https://github.com/bpftrace/bpftrace/pull/2096) - Fix probe matching for uprobes with absolute address - [#2138](https://github.com/bpftrace/bpftrace/pull/2138) - Fix tools to work on new kernel versions - [#2136](https://github.com/bpftrace/bpftrace/pull/2136) - Fix uprobe target resolution - [#2180](https://github.com/bpftrace/bpftrace/pull/2180) - Fix using wildcards in kfunc - [#2208](https://github.com/bpftrace/bpftrace/pull/2208) - Improve handling of format strings - [#2164](https://github.com/bpftrace/bpftrace/pull/2164) - Fix codegen for buf - [#2217](https://github.com/bpftrace/bpftrace/pull/2217) #### Tools - Update biosnoop.bt for kernel >=5.17 - [#2207](https://github.com/bpftrace/bpftrace/pull/2207) ## [0.14.1] 2021-12-29 #### Fixed - Fix precedence of multiplicative operations - [#2096](https://github.com/bpftrace/bpftrace/pull/2096) ## [0.14.0] 2021-10-22 #### Added - Build time dependency on cereal - [#1893](https://github.com/bpftrace/bpftrace/pull/1893) - Build time dependency on asciidoctor for man page generation - [#1927] (https://github.com/bpftrace/bpftrace/pull/1927) - Support microsecond timestamps in stftime() - [#1922](https://github.com/bpftrace/bpftrace/pull/1922) - Add `_` as integer literal digit separator - [#1900](https://github.com/bpftrace/bpftrace/pull/1900) - Support for C style integer suffix in parser - [#1938](https://github.com/bpftrace/bpftrace/pull/1938) - Add C like pointer arithmetic - [#1881](https://github.com/bpftrace/bpftrace/pull/1881) - Automatic resolution of library paths for uprobes - [#1971](https://github.com/bpftrace/bpftrace/pull/1971) - Support positional parameters as integer literals - [#1982](https://github.com/bpftrace/bpftrace/pull/1982) - Access to uprobe arguments by name - [#1994](https://github.com/bpftrace/bpftrace/pull/1994) - Support variable strings size - [#2044](https://github.com/bpftrace/bpftrace/pull/2044) #### Changed - Prevent LLVM from unrolling loops - [#1967](https://github.com/bpftrace/bpftrace/pull/1967) #### Deprecated #### Removed #### Fixed - Fix memory leaks in struct types - [#1885](https://github.com/bpftrace/bpftrace/pull/1885) - Fix strncmp() when N is bigger than on-stack buffer - [#1974](https://github.com/bpftrace/bpftrace/pull/1974) - Fix strncmp() to check for NUL terminator - [#1974](https://github.com/bpftrace/bpftrace/pull/1974) - Fix unroll() with async calls - [#1972](https://github.com/bpftrace/bpftrace/pull/1972) - Fix string comparison codegen - [#1979](https://github.com/bpftrace/bpftrace/pull/1979) - Fix verifier error when accessing same tracepoint field twice - [#2008](https://github.com/bpftrace/bpftrace/pull/2008) - Fix reading too many bits for <64 bit kfunc args - [#2014](https://github.com/bpftrace/bpftrace/pull/2014) - Fix misaligned stack access for map keys - [#2012](https://github.com/bpftrace/bpftrace/pull/2012) #### Tools #### Documentation - Write new man page for `bpftrace(8)` - [#1711](https://github.com/bpftrace/bpftrace/pull/1711) ## [0.13.1] 2021-12-29 #### Fixed - Fix precedence of multiplicative operations - [#2096](https://github.com/bpftrace/bpftrace/pull/2096) ## [0.13.0] 2021-07-01 #### Added - Warn if attaching a kprobe to a non-traceable function - [#1835](https://github.com/bpftrace/bpftrace/pull/1835) - Support for `-k[k]` and `elapsed` in `iter` probes - [#1882](https://github.com/bpftrace/bpftrace/pull/1882) #### Changed - Disallow accessing common tracepoint fields - [#1810](https://github.com/bpftrace/bpftrace/pull/1810) - Improve JSON printing (nested structs) - [#1778](https://github.com/bpftrace/bpftrace/pull/1778) - Return 1 from tracepoint probes - [#1857](https://github.com/bpftrace/bpftrace/pull/1857) - Preserve original order of struct types - [#1850](https://github.com/bpftrace/bpftrace/pull/1850) - Forbid casting from/to struct types - [#1873](https://github.com/bpftrace/bpftrace/pull/1873) #### Deprecated #### Removed #### Fixed - Fix single arg wildcard probe listing - [#1775](https://github.com/bpftrace/bpftrace/pull/1775) - Fix --info reporting wrong libbpf build info - [#1776](https://github.com/bpftrace/bpftrace/pull/1776) - Reduce frequency of lost stack traces - [#1812](https://github.com/bpftrace/bpftrace/pull/1812) - Make kaddr() report failure for unknown kernel symbols - [#1836](https://github.com/bpftrace/bpftrace/pull/1836) - Fix false non-traceable function warnings - [#1866](https://github.com/bpftrace/bpftrace/pull/1866) - Fix memory leak in clang parser - [#1878](https://github.com/bpftrace/bpftrace/pull/1878) #### Tools #### Documentation ## [0.12.1] 2021-04-16 Incorrect --info output bug fix release ## [0.12.0] 2021-04-01 #### Added - Add path builtin - [#1492](https://github.com/bpftrace/bpftrace/pull/1492) - Allow wildcards for tracepoint categories - [#1445](https://github.com/bpftrace/bpftrace/pull/1445) - Add wildcard support for kfunc probe types - [#1410](https://github.com/bpftrace/bpftrace/pull/1410) - Add builtin function: `strftime` - [#1387](https://github.com/bpftrace/bpftrace/pull/1387) - Fix `printf` not allowing format specifiers to be directly followed by alphabetic characters - [#1414](https://github.com/bpftrace/bpftrace/pull/1414) - Fix `top` and `div` arguments of `print()` not working for Type::avg maps - [#1416](https://github.com/bpftrace/bpftrace/pull/1416) - Add an option to disable warning messages - [#1444](https://github.com/bpftrace/bpftrace/pull/1444) - Support scientific notation for integer literals - [#1476](https://github.com/bpftrace/bpftrace/pull/1476) - List retprobes - [#1484](https://github.com/bpftrace/bpftrace/pull/1484) - Resolve unknown typedefs using BTF and give a hint when a type cannot be found - [#1485](https://github.com/bpftrace/bpftrace/pull/1485) - Support multi-matched globbed targets for uprobe and ustd probes - [#1499](https://github.com/bpftrace/bpftrace/pull/1499) - Positional parameters: support numbers as strings and params as string literals - [#1514](https://github.com/bpftrace/bpftrace/pull/1514) - Support for tracepoint __data_loc fields - [#1542](https://github.com/bpftrace/bpftrace/pull/1542) - Set addrspace info for various builtins - [#1504](https://github.com/bpftrace/bpftrace/pull/1504) - Support watchpoint for kernel space address - [#1552](https://github.com/bpftrace/bpftrace/pull/1552) - Support for pointer to pointer - [#1557](https://github.com/bpftrace/bpftrace/pull/1557) - Support for uprobe refcounts - [#1567](https://github.com/bpftrace/bpftrace/pull/1567) - Add basic options and documentations for fuzzing - [#1601](https://github.com/bpftrace/bpftrace/pull/1601) - Disable `str($# + 1)` - [#1619](https://github.com/bpftrace/bpftrace/issues/1619) - Array improvements (support assignment to variables and usage as a map key) - [#1656](https://github.com/bpftrace/bpftrace/pull/1656) - Add builtin function: `macaddr` - [#1647](https://github.com/bpftrace/bpftrace/pull/1647) - Add support for usdt arguments utilising the index register and scale - [#1684](https://github.com/bpftrace/bpftrace/pull/1684) - Add basic mips64 support - [#1599](https://github.com/bpftrace/bpftrace/pull/1599) - Printing structures - [#1705](https://github.com/bpftrace/bpftrace/pull/1705) - Array indexing on pointers - [#1739](https://github.com/bpftrace/bpftrace/pull/1739) #### Changed - Warn if using `print` on `stats` maps with top and div arguments - [#1433](https://github.com/bpftrace/bpftrace/pull/1433) - Prefer BTF data if available to resolve tracepoint arguments - [#1439](https://github.com/bpftrace/bpftrace/pull/1439) - Improve error messages for kfunc probe types - [#1451](https://github.com/bpftrace/bpftrace/pull/1451) - Better handling of empty usdt namespaces - [#1486](https://github.com/bpftrace/bpftrace/pull/1486) - Switch `nsecs` to `ktime_get_boot_ns` - [#1475](https://github.com/bpftrace/bpftrace/pull/1475) - Tracepoint __data_loc fields are renamed from `args->data_loc_name` to `args->name` - [#1542](https://github.com/bpftrace/bpftrace/pull/1542) - Change a part of the message of '-v' output - [#1553](https://github.com/bpftrace/bpftrace/pull/1553) - Improve tuple assignment error message - [#1563](https://github.com/bpftrace/bpftrace/pull/1563) - Remove "BTF: using data from ..." message when using -v flag - [#1554](https://github.com/bpftrace/bpftrace/pull/1554) - Add -q option for quiet - [#1616](https://github.com/bpftrace/bpftrace/pull/1616) - Optimize unknown/incomplete types resolution - [#1571](https://github.com/bpftrace/bpftrace/pull/1571) - Do not check size of the format string of `printf` - [#1538](https://github.com/bpftrace/bpftrace/pull/1538) - Unify semantics of wildcards in probe listing and attachement - [#1549](https://github.com/bpftrace/bpftrace/pull/1549) - Improve codegen for structs and arrays - [#1705](https://github.com/bpftrace/bpftrace/pull/1705) - Do not unpack in-kernel headers if system has BTF - [#1740](https://github.com/bpftrace/bpftrace/pull/1740) #### Deprecated #### Removed - Disable some kfunc probes whose tracing crashes - [#1432](https://github.com/bpftrace/bpftrace/pull/1432) #### Fixed - Fix negative overflow bug and unstable tests in PR #1416 - [#1436](https://github.com/bpftrace/bpftrace/pull/1436) - Fix `print` outputs nothing when used on hist() maps with large top args - [#1437](https://github.com/bpftrace/bpftrace/pull/1437) - Fix array indexing regression - [#1457](https://github.com/bpftrace/bpftrace/pull/1457) - Fix type resolution for struct field access via variables - [#1450](https://github.com/bpftrace/bpftrace/pull/1450) - Fix wrong setting of vmlinux_location.raw when offset kprobe used - [#1530](https://github.com/bpftrace/bpftrace/pull/1530) - Fix pointer arithmetic for positional parameters - [#1514](https://github.com/bpftrace/bpftrace/pull/1514) - SEGV when using perf format for stacks - [#1524](https://github.com/bpftrace/bpftrace/pull/1524) - Fix llvm errors of PositonalParameter - [#1565](https://github.com/bpftrace/bpftrace/pull/1565) - Error if Positional Params num is zero - [#1568](https://github.com/bpftrace/bpftrace/issues/1568) - Fix LNOT - [#1570](https://github.com/bpftrace/bpftrace/pull/1570) - Fix invalid cast handling in tuple - [#1572](https://github.com/bpftrace/bpftrace/pull/1572) - Check string comparison size - [#1573](https://github.com/bpftrace/bpftrace/pull/1573) - Fix a possible integer overflow - [#1580](https://github.com/bpftrace/bpftrace/pull/1580) - Printing of small integers with `printf` - [#1532](https://github.com/bpftrace/bpftrace/pull/1532) - Fix bitfield access for big endian - [#1628](https://github.com/bpftrace/bpftrace/pull/1628) - Error if using negative length in str() and buf() - [#1621](https://github.com/bpftrace/bpftrace/pull/1621) - Only create int type Identifier when it is used in sizeof() - [#1622](https://github.com/bpftrace/bpftrace/pull/1622) - Check exponent value can be expressed in uint64_t - [#1623](https://github.com/bpftrace/bpftrace/pull/1623) - Fix tracing of usdt probes across namespaces - [#1637](https://github.com/bpftrace/bpftrace/pull/1637) - Disable reg() for kfunc - [#1646](https://github.com/bpftrace/bpftrace/pull/1646) - Fix several undefined behavior - [#1645](https://github.com/bpftrace/bpftrace/pull/1645) - Fix invalid size crash when using strftime() inside a tuple - [#1658](https://github.com/bpftrace/bpftrace/pull/1658) - Don't create a tuple if an element size if zero - [#1653](https://github.com/bpftrace/bpftrace/pull/1653) - Support clear() and delete() on a count()-based map without a key - [#1639](https://github.com/bpftrace/bpftrace/pull/1639) - Add workaround for too deep or long macros - [#1650](https://github.com/bpftrace/bpftrace/pull/1650) - Fix attaching to usdt probes in shared libraries - [#1600](https://github.com/bpftrace/bpftrace/pull/1600) - Fix attaching to multiple usdt probe locations with the same label - [#1681](https://github.com/bpftrace/bpftrace/pull/1681) - Fix signed extension of usdt arguments to the internal 64-bit integer type - [#1684](https://github.com/bpftrace/bpftrace/pull/1684) #### Tools - Hook up execsnoop.bt script onto `execveat` call - [#1490](https://github.com/bpftrace/bpftrace/pull/1490) - Support new capabilities for capable.bt - [#1498](https://github.com/bpftrace/bpftrace/pull/1498) - Add disk field to biosnoop - [#1660](https://github.com/bpftrace/bpftrace/pull/1660) #### Documentation - Document uptr() and kptr() function - [#1626](https://github.com/bpftrace/bpftrace/pull/1626) ## [0.11.4] 2020-11-14 Alpine build bug fix release ## [0.11.3] 2020-11-13 bcc 0.17 support release ### Changed Detect 7 arg bpf_attach_uprobe() API - [#1589](https://github.com/bpftrace/bpftrace/pull/1589) ## [0.11.2] 2020-10-30 LLVM 11 support release ### Added Add LLVM11 build support - [#1578](https://github.com/bpftrace/bpftrace/pull/1578) ## [0.11.1] 2020-09-22 Bug fix release for the [Docker build](https://quay.io/repository/bpftrace/bpftrace) ### Fixed - Don't strip END_trigger - [#1513](https://github.com/bpftrace/bpftrace/pull/1513) ## [0.11.0] 2020-07-15 ### All Changes #### Added - Allow uprobe placement on arbitrary addresses when --unsafe is used - [#1388](https://github.com/bpftrace/bpftrace/pull/1388) - Support for s390x - [#1241](https://github.com/bpftrace/bpftrace/pull/1241) - `buf` a new function that makes it possible to safely print arbitrary binary data - [#1107](https://github.com/bpftrace/bpftrace/pull/1107) - A new function, `sizeof`, which returns the size of an expression, similar to `sizeof` in C - [#1269](https://github.com/bpftrace/bpftrace/pull/1269) - C style while loop support, `while ($a < 100) { $a++ }` - [#1066](https://github.com/bpftrace/bpftrace/pull/1066) - Using a BTF enum value will pull in the entire enum definition - [#1274](https://github.com/bpftrace/bpftrace/pull/1274) - Add support of using positional params in unroll and increase the unroll limit to 100 - [#1286](https://github.com/bpftrace/bpftrace/pull/1286) - Support for piping scripts in via stdin - [#1310](https://github.com/bpftrace/bpftrace/pull/1310) - Don't require if --btf is specified - [#1315](https://github.com/bpftrace/bpftrace/pull/1315) - Silence errors about `modprobe` not being found - [#1314](https://github.com/bpftrace/bpftrace/pull/1314) - With --btf, do not use for resolving tracepoint defs - [#1318](https://github.com/bpftrace/bpftrace/pull/1318) - Add environment variable, BPFTRACE_PERF_RB_PAGES, to tune perf ring buffer size - [#1329](https://github.com/bpftrace/bpftrace/pull/1329) - Add --usdt-file-activation to activate usdt semaphores by file name - [#1317](https://github.com/bpftrace/bpftrace/pull/1317) - Introduce `-k` and `-kk` options. Emit a warning when a bpf helper returns an error - [#1276](https://github.com/bpftrace/bpftrace/pull/1276) - Add tuples to language - [#1326](https://github.com/bpftrace/bpftrace/pull/1326) - Add support for listing struct/union/enum definitions using BTF - [#1340](https://github.com/bpftrace/bpftrace/pull/1340) - Add libbpf build into in --info - [#1367](https://github.com/bpftrace/bpftrace/pull/1367) - Add support for time units `us` and `hz` for probe `interval` - [#1377](https://github.com/bpftrace/bpftrace/pull/1377) - Add support for non-map print() - [#1381](https://github.com/bpftrace/bpftrace/pull/1381) - Enable `printf`, `cat` and `system` to have more than 7 arguments - [#1404](https://github.com/bpftrace/bpftrace/pull/1404) - Enable the `ternary` operator to evaluate builtin calls - [#1405](https://github.com/bpftrace/bpftrace/pull/1405) #### Changed - Require C++17 and CMake 3.8 for building bpftrace - [#1200](https://github.com/bpftrace/bpftrace/pull/1200) - [#1259](https://github.com/bpftrace/bpftrace/pull/1259) - Allow positional parameters in probe attachpoint definitions - [#1328](https://github.com/bpftrace/bpftrace/pull/1328) - Only list uprobe and usdt probes when `-p` is given - [#1340](https://github.com/bpftrace/bpftrace/pull/1340) - Remove address space memory limit - [#1358](https://github.com/bpftrace/bpftrace/pull/1358) #### Deprecated #### Removed - Drop LLVM 5 support - [#1215](https://github.com/bpftrace/bpftrace/issues/1215) - Remove the --btf option - [#1669](https://github.com/bpftrace/bpftrace/pull/1669) #### Fixed - Various big endian related fixes - [#1241](https://github.com/bpftrace/bpftrace/pull/1241) - Type check the `cond` of if and ternary statements - [#1229](https://github.com/bpftrace/bpftrace/pull/1229) - Fix usdt reads in various architecture - [#1325](https://github.com/bpftrace/bpftrace/pull/1325) - Attach to duplicated USDT markers - [#1341](https://github.com/bpftrace/bpftrace/pull/1341) - Fix `KBUILD_MODNAME` - [#1352](https://github.com/bpftrace/bpftrace/pull/1352) - Fix `ntop()` not accepting tracepoint arguments - [#1365](https://github.com/bpftrace/bpftrace/pull/1365) - Fix attaching to usdt probes in multiple binaries - [#1356](https://github.com/bpftrace/bpftrace/pull/1356) - Decrement usdt semaphore count after bpftrace execution - [#1370](https://github.com/bpftrace/bpftrace/pull/1370) - Reduce high memory consumption when using usdt semaphore - [#1374](https://github.com/bpftrace/bpftrace/pull/1374) - Remove registers that are not in struct pt_regs (x86-64) - [#1383](https://github.com/bpftrace/bpftrace/issues/1383) - Ignore trailing kernel module annotation for k[ret]probe's - [#1413](https://github.com/bpftrace/bpftrace/pull/1413) #### Tools #### Documentation - Clean up README - [#1273](https://github.com/bpftrace/bpftrace/pull/1273) - Add missing `struct` keyword to examples in the one liner tutorial - [#1275](https://github.com/bpftrace/bpftrace/pull/1275) ## [0.10.0] 2020-04-12 ### Highlights #### kfuncs Improved kprobes which are near zero overhead and use BTF to derive argument names and types: ``` bpftrace -e 'kfunc:fget { printf("fd %d\n", args->fd); }' ``` #### C++ Symbol demangling bpftrace can now demangle C++ symbols in binaries: ``` bpftrace -e 'uprobe:./a.out:"foo()" {printf("ok\n");} ``` #### if else control flow Support for `if else` has been added, making it possible to write: ``` if (cond) { ... } else if (cond) { ... } ``` Instead of: ``` if (cond) { ... } else { if (cond) { ... } } ``` #### LLVM 9 & 10 Support for LLVM 9 and LLVM 10 has been added. #### Docker images Docker images containing a static build are now available on [quay.io](https://quay.io/repository/bpftrace/bpftrace). ### All Changes #### Added - Add kfunc/kretfunc description to docs/reference_guide.md (e3b9518b) by Jiri Olsa <jolsa@kernel.org> - Add kfunc/kretfunc probe tests (bbf2083a) by Jiri Olsa <jolsa@kernel.org> - Add test_btf class to setup BTF data (ecbd66b7) by Jiri Olsa <jolsa@kernel.org> - Fortify exported functions with has_data check (083bcf9f) by Jiri Olsa <jolsa@kernel.org> - Detect btf feature via BTF class (a9450425) by Jiri Olsa <jolsa@kernel.org> - Add support to filter kfunc list (a98b3f02) by Jiri Olsa <jolsa@kernel.org> - List kfunc functions (75a0f9c7) by Jiri Olsa <jolsa@kernel.org> - Generate load code for kfunc/kretfunc arguments (30f699b1) by Jiri Olsa <jolsa@kernel.org> - Resolve kfunc arguments in semantic analyser (de2f6c1d) by Jiri Olsa <jolsa@kernel.org> - Resolve kfunc arguments in BTF field analyser (8cd3fb50) by Jiri Olsa <jolsa@kernel.org> - Add single_provider_type function (3a6325e5) by Jiri Olsa <jolsa@kernel.org> - Factor out builtin_args_tracepoint function (e33c246e) by Jiri Olsa <jolsa@kernel.org> - Add BTF::resolve_args function to resolve kfunc arguments (69c8fd45) by Jiri Olsa <jolsa@kernel.org> - Load and attach kfunc/kretfunc programs (126a9edd) by Jiri Olsa <jolsa@kernel.org> - Add missing ProbeType::watchpoint to probetypeName function (343165b1) by Jiri Olsa <jolsa@kernel.org> - Allow to specify kernel include dirs (1e987f45) by Jiri Olsa <jolsa@kernel.org> - Feature detect `probe_read_{kernel,user}` (b7c236f9) by bas smit <bas@baslab.org> - Add support for using demangled symbols in uretprobe names (269033de) by Masanori Misono <m.misono760@gmail.com> - Implement `else if` control flow (34fc2801) by Daniel Xu <dxu@dxuuu.xyz> - detect lockdown mode (37d28c26) by bas smit <bas@baslab.org> - Extend info flag with system/build info (73abef68) by bas smit <bas@baslab.org> - Add support for C++ mangled symbols in uprobe names #687 (e8656cbd) by Augusto Caringi <acaringi@redhat.com> #### Changed - Allow hex/octal positional parameters (ef20128b) by bas smit <bas@baslab.org> - Allow negative positional parameters (babf057e) by bas smit <bas@baslab.org> - Make positionalparameters literal to avoid warnings (0859fc6b) by bas smit <bas@baslab.org> - Make `exit()` terminate current probe (6334c23d) by bas smit <bas@baslab.org> - Improve an error message when trying to use 'args' other than tracepoint (e303048c) by Masanori Misono <m.misono760@gmail.com> - Disable a symbol name cache if ASLR is enabled and `-c` option is not given (4651255b) by Masanori Misono <m.misono760@gmail.com> - Remove deprecated builtins (2667b8a2) by bas smit <bas@baslab.org> #### Fixed - reject invalid pid argument (cebc5978) by bas smit <bas@baslab.org> - Fix positional parameter error (1b4febee) by bas smit <bas@baslab.org> - Emit better tracepoint parser errors (f5217821) by Daniel Xu <dxu@dxuuu.xyz> - Fix if comparison (8f8c9cb4) by Masanori Misono <m.misono760@gmail.com> - Do not keep open BEGIN/END probes (19d90057) by Jiri Olsa <jolsa@kernel.org> - Check the length of ap.mode (a388dc14) by Masanori Misono <m.misono760@gmail.com> - Fix ternary comparison (360be8cf) by Masanori Misono <m.misono760@gmail.com> - Cast LNOT result (890f5930) by Masanori Misono <m.misono760@gmail.com> - Gracefully handle long position param overflow (6f26a863) by Vlad Artamonov <742047+vladdy@users.noreply.github.com> - Error if wildcards are used in "category" of tracepoint (3bfdec94) by Masanori Misono <m.misono760@gmail.com> - Fix reading USDT probe arguments on AArch64 (ee5314ba) by Nick Gasson <nick.gasson@arm.com> - Remove type qualifiers from a cast_type (4ad2bf19) by Masanori Misono <m.misono760@gmail.com> - Fix printf argument offsets (2d2f2332) by Alastair Robertson <alastair@ajor.co.uk> - Warn if Type::string size is not matched when assignment (4638b968) by Masanori Misono <m.misono760@gmail.com> - Print Type::string and Type::array size information along with type information (03a837e7) by Masanori Misono <m.misono760@gmail.com> - Consider a short Type::string value (684513ed) by Masanori Misono <m.misono760@gmail.com> - Consider a non null-terminated Type::string value (13614152) by Masanori Misono <m.misono760@gmail.com> #### Tools - oomkill: fix kprobe arg (675727a4) by Xiaozhou Liu <liuxiaozhou@bytedance.com> - Fix 'signed operands for /' warning in naptime.bt (c8f4a6d8) by Augusto Caringi <acaringi@redhat.com> #### Documentation - Fix example links to only search bpftrace repo (71c9d29e) by Martin Schmidt <martin.schmidt@epita.fr> - Remove example link to a runtime test (560454a1) by Martin Schmidt <martin.schmidt@epita.fr> - Add link to example for interval and BEGIN/END (badf5653) by Martin Schmidt <martin.schmidt@epita.fr> - Add link to example for profile (ea6f706a) by Martin Schmidt <martin.schmidt@epita.fr> - Add links to examples for tracepoints (f6a3d26a) by Martin Schmidt <martin.schmidt@epita.fr> - Add links to examples for uprobe/uretprobe (5dd4bd8d) by Martin Schmidt <martin.schmidt@epita.fr> - Add links to examples for kprobe/kretprobe (c580ef26) by Martin Schmidt <martin.schmidt@epita.fr> - When installing from source on ubuntu and Fedora, non-root users need to add 'sudo' when executing 'make install' (3030046b) by mazhen <mz1999@gmail.com> - docs: Add documentation for integer casts (f087abbd) by Daniel Xu <dxu@dxuuu.xyz> - Docs: Fix broken link (f76b8bbb) by Adam Jensen <acjensen@gmail.com> - Docs: Add missing builtin functions (fd08a932) by Adam Jensen <acjensen@gmail.com> #### Internal - Remove codegen tests warning (f18746af) by bas smit <bas@baslab.org> - build: document libbcc linking (#1252) (4dadd515) by bas smit <bas@baslab.org> - cmake: bail on unsupported architectures (4ae387f0) by Daniel Xu <dxu@dxuuu.xyz> - Revert "Only link agains libbpf if it exists (#1247)" (04ecb731) by bas smit <bas@baslab.org> - Only link agains libbpf if it exists (#1247) (a3febcb8) by bas smit <bas@baslab.org> - Align libbpf.h (229eef6c) by bas smit <bas@baslab.org> - Sync libbpf with v5.6 (0b369fe6) by bas smit <bas@baslab.org> - Add runtime tests for ternary (2efcdb29) by Masanori Misono <m.misono760@gmail.com> - Use BPFtrace::error for TracepointFormatParser errors (#1243) (9106e10c) by Martin Schmidt <martin.schmidt@epita.fr> - codegen: Send map id instead of ident string for async events (9a063adc) by bas smit <bas@baslab.org> - ci: Add LLVM 10 (696e16ce) by Masanori Misono <m.misono760@gmail.com> - Add codegen test for LLVM 10 (33fe3ee4) by Masanori Misono <m.misono760@gmail.com> - Suppress -Winconsistent-missing-override warning (2044c53d) by Masanori Misono <m.misono760@gmail.com> - Use CreateMemCpy that takes MaybeAlign in LLVM 10 (a67fd22d) by Masanori Misono <m.misono760@gmail.com> - Don't over-read usdt arguments (1711ec70) by Daniel Xu <dxu@dxuuu.xyz> - Add proper bcc prefix for bcc headers (977d5851) by Jiri Olsa <jolsa@kernel.org> - Use urandom instead of random (23603bfc) by Masanori Misono <m.misono760@gmail.com> - tests: fix llmv 5 tests (449b33a4) by bas smit <bas@baslab.org> - codegen: correctly copy and "usym" map (f7a9d9e2) by bas smit <bas@baslab.org> - codegen: Use map type in perf_event signature (0a27eeb5) by bas smit <bas@baslab.org> - codegen: avoid usym copy on map assignment (25116d21) by bas smit <bas@baslab.org> - codegen: deduplicate usym code (078a8236) by bas smit <bas@baslab.org> - codegen: fix `strncmp` type issues (e523e2c7) by bas smit <bas@baslab.org> - codegen: ensure `getmapkey` stores with equal types (1822cfde) by bas smit <bas@baslab.org> - codegen: fix deleteElem typing issues (0c6403bc) by bas smit <bas@baslab.org> - codegen: clang-format `join` (e641b115) by bas smit <bas@baslab.org> - codegen: memset takes an i8 value (d2a70f98) by bas smit <bas@baslab.org> - codegen: remove useless literal handling from `signal` (3bbbfe24) by bas smit <bas@baslab.org> - codegen: fix `probe_read` typing issue (eca43df2) by bas smit <bas@baslab.org> - codegen: fix sarg type issue (09152138) by bas smit <bas@baslab.org> - codegen: fix `probe_read_str` typing issues (914c87e2) by bas smit <bas@baslab.org> - codegen: fix reg typing issue (0c66f2f5) by bas smit <bas@baslab.org> - parser: Do not remove empty probe arguments (ae4fe7fb) by Daniel Xu <dxu@dxuuu.xyz> - cmake: Link against libz when searching for btf_dump__new (6323d8fb) by Daniel Xu <dxu@dxuuu.xyz> - snapcraft: add arm64 to build architectures (2b0faa3e) by Colin Ian King <colin.king@canonical.com> - cmake: Control manpages generation (ef39ed0f) by Ovidiu Panait <ovpanait@gmail.com> - Don't check str arg length for cgroupid (aa94d9b3) by Chris Hunt <chrahunt@gmail.com> - Track current function name during analysis (d1f23cab) by Chris Hunt <chrahunt@gmail.com> - Remove unused srclines_ (ce9c4179) by Chris Hunt <chrahunt@gmail.com> - Remove unused print_map_lhist (7c32b827) by Chris Hunt <chrahunt@gmail.com> - Remove leftover print_hist declaration (63f4f029) by Chris Hunt <chrahunt@gmail.com> - Remove leftover print_lhist declaration (8008c5a9) by Chris Hunt <chrahunt@gmail.com> - Add apt-transport-https for xenial build (8bcf0c04) by Dale Hamel <dale.hamel@shopify.com> - Add the snapcraft yaml rules to allow bpftrace to be built as a snap. (c2eceeb3) by Colin Ian King <colin.king@canonical.com> - Revert "Require C++17 to build" (24f97308) by bas smit <bas@baslab.org> - Fix tracepoint expansion regression (b4f0c204) by Daniel Xu <dxu@dxuuu.xyz> - codegen: fix `map` typing (11814f29) by bas smit <bas@baslab.org> - codegen: Update LLVM5 codegen tests (c4f147d3) by bas smit <bas@baslab.org> - codegen: fix argX type issue (c04bad20) by bas smit <bas@baslab.org> - codegen: Fix comm typing issues (5926429d) by bas smit <bas@baslab.org> - codegen: Fix stackid typing issues (a7ba4a1e) by bas smit <bas@baslab.org> - codegen: Fix exit typing issues (f676b9c5) by bas smit <bas@baslab.org> - codegen: Fix ntop typing issues (ac792f58) by bas smit <bas@baslab.org> - codegen: Fix usym typing issues (2e84b52d) by bas smit <bas@baslab.org> - irbuilder: Add struct storage (1fbccf1b) by bas smit <bas@baslab.org> - Strengthen tracepoint format parsing (a2e3d5db) by Jiri Olsa <jolsa@kernel.org> - cmake: use *_LIBRARIES when testing for libbfd version (b1200771) by Daniel Xu <dxu@dxuuu.xyz> - Handle escaped double quotes in AttachPointParser (b98b281d) by Daniel Xu <dxu@dxuuu.xyz> - Support `:`s in quoted string (c230fc42) by Daniel Xu <dxu@dxuuu.xyz> - Add parser tests for trailing non-numeric characters (c0b8644f) by Daniel Xu <dxu@dxuuu.xyz> - Update AttachPoint::name to print out watchpoints correctly (dd2312c7) by Daniel Xu <dxu@dxuuu.xyz> - Unify ast::AttachPoint::addr and ast::AttachPoint::address (71f4205f) by Daniel Xu <dxu@dxuuu.xyz> - Fix semantic analyser unit tests (39d4a493) by Daniel Xu <dxu@dxuuu.xyz> - Fix runtime tests (1612af97) by Daniel Xu <dxu@dxuuu.xyz> - Update callee interfaces (78c04b01) by Daniel Xu <dxu@dxuuu.xyz> - Move AttachPoint parsing logic out of bison (43a72e37) by Daniel Xu <dxu@dxuuu.xyz> - tests: cmake: Fix build with ninja (f1fc5190) by Ovidiu Panait <ovpanait@gmail.com> - bpffeature: move macros to header (860ac6d4) by bas smit <bas@baslab.org> - bpffeature: delete copy/move constructors/assign (ac5e0025) by bas smit <bas@baslab.org> - bpffeature: cleanup `report` (af780eb1) by bas smit <bas@baslab.org> - bpffeature: detect supported program types (ce5bbb78) by bas smit <bas@baslab.org> - bpffeature: detect supported map types (437df58d) by bas smit <bas@baslab.org> - bpffeature: remove boilerplate (ac4ad41c) by bas smit <bas@baslab.org> - Avoid calling "slow" regex constructor (fc88784e) by bas smit <bas@baslab.org> - CreateMemSet: llvm10: Fix compilation errors (6f81111c) by Ovidiu Panait <ovidiu.panait@windriver.com> - Discard return value for emitAndFinalize() (29caf4b7) by Daniel Xu <dxu@dxuuu.xyz> - Require C++17 to build (458bf66d) by Daniel Xu <dxu@dxuuu.xyz> - Make docker run command more generic (#1182) (c67730c4) by Connor Nelson <Connor@ConnorNelson.com> - Use host network when building docker image (23c29ff1) by Daniel Xu <dxu@dxuuu.xyz> - fix typo (92f25f95) by zeil <nonamezeil@gmail.com> - Resolve USDT binaries relative to mount namespace (3bb4a9fd) by Dale Hamel <dale.hamel@shopify.com> - Add docker images as options in install.md (30756be7) by Dale Hamel <dale.hamel@srvthe.net> - Add "edge" build, push master to :latest and :edge (b0e6bdc7) by Dale Hamel <dale.hamel@shopify.com> - ast: add missing parameter name (f156a0fb) by bas smit <bas@baslab.org> - Add the Japanese translation version of the one-liner tutorial (78621fb1) by Masanori Misono <m.misono760@gmail.com> - Revert "No need to promote scalars to 64-bit" (9a9d1451) by Dale Hamel <dale.hamel@shopify.com> - fix build error (ed48e795) by bas smit <bas@baslab.org> - Make BEFORE clause in runtime tests synchronous (77f93dbc) by Dale Hamel <dale.hamel@shopify.com> - Only need to rebuild codegen tests if C++ files change (d0792c06) by Alastair Robertson <alastair@ajor.co.uk> - Replace tabs with spaces (f4e377a1) by Alastair Robertson <alastair@ajor.co.uk> - No need to promote scalars to 64-bit (8af25ae9) by Alastair Robertson <alastair@ajor.co.uk> - Regenerate codegen_includes.cpp when files it references are updated (d6d0e836) by Alastair Robertson <alastair@ajor.co.uk> - ci: Add LLVM 9 (42dab3c9) by bas smit <bas@baslab.org> - codegen: add LLVM-9 rewriter exceptions (681d1850) by bas smit <bas@baslab.org> - codegen: LLVM9 rewriter (3ec8af95) by bas smit <bas@baslab.org> - codegen: Rewrite tests (aefc89e7) by bas smit <bas@baslab.org> - codegen: Remove version dependence from codegen (cd3ab819) by bas smit <bas@baslab.org> - Add STATIC_LIBC=ON to Docker build scripts (6ef3af3c) by Alastair Robertson <alastair@ajor.co.uk> - Support pushing docker images to quay.io from github actions (b8ab21ae) by Dale Hamel <dale.hamel@shopify.com> - Add xenial to CI build (153e61ef) by Ace Eldeib <alexeldeib@gmail.com> - Only send IRC notifications for build failures on master (471e79b7) by Alastair Robertson <alastair@ajor.co.uk> - vagrant: fix formatting (e8a14566) by bas smit <bas@baslab.org> - vagrant: Add fedora 31 (c2354a78) by bas smit <bas@baslab.org> - vagrant: Update ubuntu boxes (9610895c) by bas smit <bas@baslab.org> - Add Dockerfile.release for bpftrace docker image on quay.io (c2568ee5) by Dale Hamel <dale.hamel@shopify.com> - Mark context accesses as volatile (56d4721e) by Masanori Misono <m.misono760@gmail.com> - Cast ctx automatically depending on a program type (0e4282e1) by Masanori Misono <m.misono760@gmail.com> - Access context fields directly (3a910814) by Masanori Misono <m.misono760@gmail.com> - Error if trying to use context as a map key/value (b7d2510b) by Masanori Misono <m.misono760@gmail.com> - Introduce Type::ctx (f05b4cda) by Masanori Misono <m.misono760@gmail.com> - No need to check result of check_assignment (f04c1ad9) by Alastair Robertson <alastair@ajor.co.uk> - Add workaround to remove duplicate entries in uprobe symbols listing (8f5e90f4) by Augusto Caringi <acaringi@redhat.com> - cmake: add GNUInstallDirs support (2f380013) by Ovidiu Panait <ovpanait@gmail.com> - Allow running tests as non-root (again) (efa2da20) by Daniel Xu <dxu@dxuuu.xyz> - Report kernel instruction limit (76770de3) by bas smit <bas@baslab.org> - Add missing include to btf.h (145041ec) by Augusto Caringi <acaringi@redhat.com> ## [0.9.4] 2020-02-04 ### Highlights - New calls: `signal`, `override`, `strncmp` - Support for attaching to `kprobes` at an offset - Support for struct bitfields ### All Changes #### Added - Add support to attach kprobe to offset (e31e398) by Masanori Misono <m.misono760@gmail.com> - Add `--info` flag (afafbf5) by bas smit <bas@baslab.org> - Mark 'override_return' as unsafe (49cd031) by bas smit <bas@baslab.org> - Implement bpf_override_return (784c64e) by bas smit <bas@baslab.org> - arch: Add support for powerpc64 registers (472f5ed) by Sandipan Das <sandipan@linux.ibm.com> - Add source line information to error messages (46e62c0) by bas smit <bas@baslab.org> - Support octal and hexadecimal escape sequences in string (873d7ba) by Masanori Misono <m.misono760@gmail.com> - Implement `signal` (32bb577) by bas smit <bas@baslab.org> - Make `signal` unsafe (be676b5) by bas smit <bas@baslab.org> - Implement strncmp (a1d0263) by Jay Kamat <jaygkamat@gmail.com> - Add builtin: cpid (cae4dcf) by bas smit <bas@baslab.org> - Allow uprobe offset on quoted attach points (6432609) by bas smit <bas@baslab.org> - Allow string literals as signal specifiers (0230f98) by bas smit <bas@baslab.org> - Implement bitfield support (8822cc2) by Daniel Xu <dxu@dxuuu.xyz> #### Changed - Take first binary match for PATH lookup on uprobe and USDT (ec5c2c3) by Daniel Xu <dxu@dxuuu.xyz> - Infer `uaddr` pointer type from ELF symbol size (59b0659) by bas smit <bas@baslab.org> - Rename `override_return` to `override` (96cb4b5) by bas smit <bas@baslab.org> - Runtime feature testing (17f3c82) by bas smit <bas@baslab.org> - Silenced unsigned/signed comparison warning (75101f9) by Daniel Xu <dxu@dxuuu.xyz> - error message for verification buffer size (41c0ab8) by Gordon Marler <gmarler@bloomberg.net> - Reimplement `elapsed` using a hidden map (2613ea6) by bas smit <bas@baslab.org> - Remove dependency on 'command' shell builtin (3f7a94a) by Adam Jensen <acjensen@gmail.com> - Make parsing fail if lexing fails (d092cb1) by Alastair Robertson <alastair@ajor.co.uk> - Limit increment/decrement to variables (c126441) by bas smit <bas@baslab.org> - Only warn about missing BTF info in debug mode (f84ae5c) by Daniel Xu <dxu@dxuuu.xyz> - Allow uretprobe at an address (f0785b5) by bas smit <bas@baslab.org> - fix uprobe address on short name (f7ed963) by bas smit <bas@baslab.org> - Reverse return value of strncmp (384640e) by Jay Kamat <jaygkamat@gmail.com> - Make strcmp return 0 on match (8d9069c) by bas smit <bas@baslab.org> - Differentiate between regular structs and typedef'd structs (8d34209) by Alastair Robertson <alastair@ajor.co.uk> #### Fixed - Support "." in attach point function argument (c532159) by Daniel Xu <dxu@dxuuu.xyz> - clang_parser: workaround for asm_inline in 5.4+ kernel headers (c30e4dd) by Andreas Gerstmayr <agerstmayr@redhat.com> - Consider signed array (9bb6a8b) by Masanori Misono <m.misono760@gmail.com> - Support anonymous struct/union in BTF::type_of() (36d9914) by Masanori Misono <m.misono760@gmail.com> - Allow resolving binary paths in different mount ns (124e569) by Dale Hamel <dale.hamel@shopify.com> - Avoid useless allocations in strncmp (551664e) by bas smit <bas@baslab.org> - Avoid comparing past string length (b10dc32) by bas smit <bas@baslab.org> - Call llvm.lifetime.end after memcpy if the expression is not a variable (8b2d219) by Masanori Misono <m.misono760@gmail.com> - bug: Strip newlines from log message (361d1fc) by bas smit <bas@baslab.org> - Fix buggy signed binop warnings (e87897c) by Daniel Xu <dxu@dxuuu.xyz> - Reuse `cat` and `system` ID when expanding probes (79aada5) by bas smit <bas@baslab.org> - Remove unneeded `probe_read`s from `strcmp` (43b4e4c) by bas smit <bas@baslab.org> - Fix func variable in uprobe (d864f18) by Masanori Misono <m.misono760@gmail.com> - Add space for the error message about kernel.perf_event_max_stack (de2a7a8) by Kenta Tada <Kenta.Tada@sony.com> - Improve uprobe/usdt visitor error handling and messaging (5005902) by Adam Jensen <acjensen@gmail.com> - Fix some semantic analyser crashes (b11dc75) by Alastair Robertson <alastair@ajor.co.uk> - Fix codegen for modulo operation (fe0ed5a) by Daniel Xu <dxu@dxuuu.xyz> #### Documentation - Document `override_return` (b83b51d) by bas smit <bas@baslab.org> - Add documentation on BTF (6623f25) by Masanori Misono <m.misono760@gmail.com> - docs: limit to 105 chars (91e9dad) by bas smit <bas@baslab.org> - docs: Remove double shebang (da8b10c) by bas smit <bas@baslab.org> - docs: improve readability of code snippets (34a394a) by bas smit <bas@baslab.org> - docs: remove unneeded html elements (06d8662) by bas smit <bas@baslab.org> - Fix typos (e5ad6b9) by Michael Prokop <michael.prokop@synpro.solutions> - One-liner tutorial: Use "struct" when casting (7a5624c) by Alastair Robertson <alastair@ajor.co.uk> - docs: Add centos 7 repo (1b4cb8f) by bas smit <bas@baslab.org> - docs: Fix typo (b38dbd0) by bas smit <bas@baslab.org> - Move debug flags closer to each other in help message (df61049) by Daniel Xu <dxu@dxuuu.xyz> - Add binutils dependency to documentation (c57204c) by Daniel Xu <dxu@dxuuu.xyz> - Add documentation on release procedure (#981) (528fd6e) by Daniel Xu <dxu@dxuuu.xyz> - fix: Minor spelling correction (b3a6aee) by Jason Wohlgemuth <jhwohlgemuth@users.noreply.github.com> - Document `signal` (d5f3c75) by bas smit <bas@baslab.org> - INSTALL.md: Fix TOC link (1ab0a71) by Alastair Robertson <alastair@ajor.co.uk> - Amend sizes in documentation and provide date (ddd10fe) by Dale Hamel <dale.hamel@shopify.com> - Docs: add missing TOC entry (8c1d4e9) by bas smit <bas@baslab.org> - Add the chinese version for one liners tutorial (15a930e) by supersojo <suyanjun218@163.com> #### Internal - Reorganize tests/ directory (193177b) by Daniel Xu <dxu@dxuuu.xyz> - Fix typing issues in `CreateMapUpdateElem` (e86b9bb) by bas smit <bas@baslab.org> - Fix typing issues in `CreateMapLookup` (14af118) by bas smit <bas@baslab.org> - Fix build: Add namespace to BPF_FUNC_override_return (b6de734) by Alastair Robertson <alastair@ajor.co.uk> - Unify vmlinux and BTF location list (1d39776) by Masanori Misono <m.misono760@gmail.com> - Disable probe.kprobe_offset_fail_size runtime test in CI (1497434) by Masanori Misono <m.misono760@gmail.com> - fmt: update formatting in clang_parser.cpp (aefc424) by Andreas Gerstmayr <agerstmayr@redhat.com> - Use constexpr (b59c3a7) by Masanori Misono <m.misono760@gmail.com> - Make use of feature testing (b01f89c) by bas smit <bas@baslab.org> - Import libbpf (132e1ee) by bas smit <bas@baslab.org> - Rename BPFTRACE_BTF_TEST to BPFTRACE_BTF (5bbeb31) by Masanori Misono <m.misono760@gmail.com> - Add test for anonymous struct/union processing using BTF (240f59a) by Masanori Misono <m.misono760@gmail.com> - Switch tests suite to `bcc_foreach_sym` (a251477) by bas smit <bas@baslab.org> - Make resolve_binary_paths include non-executable shared objects in its return. (c3d1095) by Michał Gregorczyk <michalgr@fb.com> - Remove full static builds from travis (4fe9064) by Dale Hamel <dale.hamel@srvthe.net> - Move ast.h definitions into ast.cpp (f0dd0b4) by Daniel Xu <dxu@dxuuu.xyz> - Use subprocess.Popen text mode (47de78b) by Daniel Xu <dxu@dxuuu.xyz> - Fix debian libclang only linking (a9a2f0f) by Dale Hamel <dale.hamel@srvthe.net> - Build static+libc images using github actions (4794aba) by Dale Hamel <dale.hamel@srvthe.net> - Enable static+glibc builds and embedding LLVM deps (b1ae710) by Dale Hamel <dale.hamel@shopify.com> - Create StderrSilencer helper class to redirect stderr (b59b97a) by Daniel Xu <dxu@dxuuu.xyz> - Add missing semicolon (add4117) by Daniel Xu <dxu@dxuuu.xyz> - ast: codegen: Add abstraction for stack pointer offset (d19614d) by Sandipan Das <sandipan@linux.ibm.com> - clang-format: avoid breaking indent in irbuilderbpf.h (5b6d236) by bas smit <bas@baslab.org> - Non-invasive formatting of src/*.h (98328f1) by Alastair Robertson <alastair@ajor.co.uk> - Clang Format: Update line-break penalties (30d5b8d) by Alastair Robertson <alastair@ajor.co.uk> - correct for clang-format check (bb30265) by Gordon Marler <gmarler@bloomberg.net> - Add requested msg prefix (f3327bd) by Gordon Marler <gmarler@bloomberg.net> - add requested changes. (c9453b5) by Gordon Marler <gmarler@bloomberg.net> - Show current log size in msg as starting point (7942b9d) by Gordon Marler <gmarler@bloomberg.net> - Fix CI clang-format (13556f9) by Daniel Xu <dxu@dxuuu.xyz> - Make ninja work with build system (76bb97a) by Daniel Xu <dxu@dxuuu.xyz> - Clang Format: switch/case bracketing style fixes (f4e46b2) by Alastair Robertson <alastair@ajor.co.uk> - Clang Format: Don't wrap braces after namespace (4b26e3f) by Alastair Robertson <alastair@ajor.co.uk> - Add non-literal strncmp test (1c41333) by bas smit <bas@baslab.org> - Rename literal test (4295985) by bas smit <bas@baslab.org> - refactor CreateMapLookupElem (7b7ab95) by bas smit <bas@baslab.org> - Add a semantic and runtime test to test task_struct field accesses (8519550) by Masanori Misono <m.misono760@gmail.com> - Use `struct task_struct` instead of `task_struct` (d39db3a) by Masanori Misono <m.misono760@gmail.com> - BTF leftover for full type rename (5088682) by Jiri Olsa <jolsa@kernel.org> - Create a single is_numeric() function in utils (374ca46) by Alastair Robertson <alastair@ajor.co.uk> - Warn if cmake is less than 3.13 when building with ASAN (ad3b9f3) by Masanori Misono <m.misono760@gmail.com> - Remove unnecessary division (81b7c0a) by Daniel Xu <dxu@dxuuu.xyz> - Add build option, BUILD_ASAN, to turn on address sanitizer (04d015e) by Daniel Xu <dxu@dxuuu.xyz> - Properly indent cmake config (24a7695) by Daniel Xu <dxu@dxuuu.xyz> - Use mocks consistently in codegen tests so they don't require root to run (b261833) by Alastair Robertson <alastair@ajor.co.uk> - Enable -Werror on CI builds (2f0f5db) by Alastair Robertson <alastair@ajor.co.uk> - CMakeLists cleanups (6b8d7ad) by Alastair Robertson <alastair@ajor.co.uk> - Disable deprecated ORCv1 warning in llvm (607b8af) by Daniel Xu <dxu@dxuuu.xyz> - Normalize code (0878020) by Daniel Xu <dxu@dxuuu.xyz> - Pass location to uprobe+offset probe (8c1a355) by bas smit <bas@baslab.org> - Use symbolic constants instead of numeric literal (457aab9) by Daniel Xu <dxu@dxuuu.xyz> - Add clang-format rule to travis CI (3b9e959) by Daniel Xu <dxu@dxuuu.xyz> - Turn off clang-format for specific long lists (bcbfaa0) by Daniel Xu <dxu@dxuuu.xyz> - Add .clang-format file (b04e478) by Daniel Xu <dxu@dxuuu.xyz> - Change reinterpret_cast to static cast and fix formatting (03d2d67) by Alastair Robertson <alastair@ajor.co.uk> - Add PER_CPU detection helper (594fd34) by bas smit <bas@baslab.org> - Store the BPF map type in the map object (2e850c5) by bas smit <bas@baslab.org> - format: align parser (b3680e6) by bas smit <bas@baslab.org> - Make ASSERTs in helper functions fail the parent testcase (ddaa482) by Alastair Robertson <alastair@ajor.co.uk> - Add dependency on testprogs and bpftrace to runtime tests (7870091) by Daniel Xu <dxu@dxuuu.xyz> - Add custom target for testprogs (d799e83) by Daniel Xu <dxu@dxuuu.xyz> - Move testprogs cmake definition before runtime test definitions (6783448) by Daniel Xu <dxu@dxuuu.xyz> - Add tests for resolve_binary_path (8fb727a) by Adam Jensen <acjensen@gmail.com> - Fix tests to run without $PATH (c1c60c2) by Adam Jensen <acjensen@gmail.com> - Add runtime tests for ambiguous wildcard matches (cca9040) by Adam Jensen <acjensen@gmail.com> - Add regression tests for modulo operation (0a1cb65) by Daniel Xu <dxu@dxuuu.xyz> - Don't take reference of a pointer (61ba68a) by Daniel Xu <dxu@dxuuu.xyz> - Silence test suite (8d1f691) by bas smit <bas@baslab.org> - Disable builtin.cgroup runtime test in CI (8277876) by Daniel Xu <dxu@dxuuu.xyz> - Add a RUNTIME_TEST_DISABLE environment to runtime tests (6c984ea) by Daniel Xu <dxu@dxuuu.xyz> - Add script to compare tool codegen between builds (d95a2d1) by bas smit <bas@baslab.org> - Minor btf cleanups (a10479b) by Daniel Xu <dxu@dxuuu.xyz> - Add FieldAnalyser to the clang parser tests (13b06d2) by Jiri Olsa <jolsa@kernel.org> - Iterate only over detected types in BTF::c_def (409d7ad) by Jiri Olsa <jolsa@kernel.org> - Add BPFtrace::btf_set_ to replace global BTF type set (06a09ca) by Jiri Olsa <jolsa@kernel.org> - Add BTF::type_of function (4378e24) by Jiri Olsa <jolsa@kernel.org> - Adding FieldAnalyser class (ec3c621) by Jiri Olsa <jolsa@kernel.org> - Move BTF object into BPFtrace class (fdf3940) by Jiri Olsa <jolsa@kernel.org> - Add runtime test (db81d25) by Daniel Xu <dxu@dxuuu.xyz> - Add clang_parser test (6cae624) by Daniel Xu <dxu@dxuuu.xyz> - Use struct instead of class (fbe3bf6) by Daniel Xu <dxu@dxuuu.xyz> - Make `strncmp` codegen unsigned (af54c9b) by bas smit <bas@baslab.org> - Avoid shift/reduce warnings (3761904) by bas smit <bas@baslab.org> - Treat stackmode as identifier (e018da5) by bas smit <bas@baslab.org> - Define all `call`s in the lexer to avoid redefinition (b8ddf25) by bas smit <bas@baslab.org> - Remove `_` suffix from local variables (34d4654) by bas smit <bas@baslab.org> - Add regression test for #957 (253cfd6) by bas smit <bas@baslab.org> - Fix paths in tests (a8dcb02) by bas smit <bas@baslab.org> - Allow runtime tests to be ran from any directory (9139bed) by bas smit <bas@baslab.org> - Link libiberty during static builds (aa8c7ba) by Daniel Xu <dxu@dxuuu.xyz> - cpid vector -> single (52ff6e3) by bas smit <bas@baslab.org> - 0.9.3 Changelog (f4ea282) by bas smit <bas@baslab.org> - Bump to 0.9.3 (3d1e022) by bas smit <bas@baslab.org> - Add `signal` tests (95cba2b) by bas smit <bas@baslab.org> - Add missing kernel option in INSTALL.md (099d1c9) by Edouard Dausque <git@edouard.dausque.net> - Make printing the LLVM IR from a debugger easier (d534295) by bas smit <bas@baslab.org> - Make `uprobes - list probes by pid` test more quiet (b2a570a) by Daniel Xu <dxu@dxuuu.xyz> - vagrant: add binutils-dev dependency (2e73e04) by Matheus Marchini <mmarchini@netflix.com> - Fix maptype bugs (028c869) by bas smit <bas@baslab.org> - Disable -Winconsistent-missing-override in mock.h (d3cb095) by Masanori Misono <m.misono760@gmail.com> - Disable -Wcast-qual for bpf/btf.h (b308a9c) by Masanori Misono <m.misono760@gmail.com> - Import used headers (979992e) by Masanori Misono <m.misono760@gmail.com> - Fix modernize-deprecated-headers warnings (b09836b) by Masanori Misono <m.misono760@gmail.com> - Fix -Wcast-align (ce45470) by Masanori Misono <m.misono760@gmail.com> - Fix -Wdelete-abstract-non-virtual-dtor (cb78da3) by Masanori Misono <m.misono760@gmail.com> - Fix -Wstring-plus-int (3e52a3d) by Masanori Misono <m.misono760@gmail.com> - Fix -Wunreachable-code-loop-increment (f354911) by Masanori Misono <m.misono760@gmail.com> - Fix -Wbraced-scalar-init (6fc82ed) by Masanori Misono <m.misono760@gmail.com> - Fix -Wmismatched-tags (e29a4f2) by Masanori Misono <m.misono760@gmail.com> - Fix -Wformat-security (cc3ef62) by Masanori Misono <m.misono760@gmail.com> - Fix some compiler warnings (9a85f10) by Daniel Xu <dxu@dxuuu.xyz> ## [0.9.3] 2019-11-22 ### Highlights - Allow attaching to uprobes at an offset - BTF support - integer casts - integer pointer casts ### All Changes #### Added - Add support to cast to a pointer of integer (#942) (8b60006) by Masanori Misono <m.misono760@gmail.com> - Add sargX builtin (9dc6024) by Adam Jensen <acjensen@gmail.com> - Add support to specify symbol with offset to uprobe (33e887f) by Jiri Olsa <jolsa@kernel.org> - add threadsnoop tool (f021967) by Brendan Gregg <bgregg@netflix.com> - add tcpsynbl tool (0cbc301) by Brendan Gregg <bgregg@netflix.com> - add tcplife tool (51d8852) by Brendan Gregg <bgregg@netflix.com> - add swapin tool (c80753b) by Brendan Gregg <bgregg@netflix.com> - add setuids tool (439311a) by Brendan Gregg <bgregg@netflix.com> - add naptime tool (572de59) by Brendan Gregg <bgregg@netflix.com> - add biostacks tool (162bc63) by Brendan Gregg <bgregg@netflix.com> - Add check if uprobe is aligned (e2c65bd) by Jiri Olsa <jolsa@kernel.org> - Support wildcards in probe path (#879) (2a361cc) by Adam Jensen <acjensen@gmail.com> - Add --btf option (ec931fa) by Jiri Olsa <jolsa@kernel.org> - Introduce int casts (ee82e64) by bas smit <bas@baslab.org> - utils: unpack kheaders.tar.xz if necessary (#768) (896fafb) by Matt Mullins <mokomull@gmail.com> - Add support to check for libbpf package (8e0800c) by Jiri Olsa <jolsa@kernel.org> - Add signed types (53cf421) by bas smit <bas@baslab.org> - Add location support to builtins (a79e5a6) by bas smit <bas@baslab.org> - Add location support to calls (c1b2a91) by bas smit <bas@baslab.org> - Add location support to the AST (67c208d) by bas smit <bas@baslab.org> - Highlight bpftrace source files (cfbaa2f) by Paul Chaignon <paul.chaignon@orange.com> - Add travis CI build icon to README.md (50375e2) by Daniel Xu <dxu@dxuuu.xyz> - Add IRC badge to README (a20af57) by Daniel Xu <dxu@dxuuu.xyz> #### Changed - Use the same shebang for all tools (78eb451) by bas smit <bas@baslab.org> - Change exit() to send SIGTERM to child processes (649cc86) by Matheus Marchini <mmarchini@netflix.com> - Make `stats` and `avg` signed (809dc46) by bas smit <bas@baslab.org> - Refactor error printer to make severity level configurable (676a6a7) by bas smit <bas@baslab.org> - Make output line-buffered by default (#894) (78e64ba) by Daniel Xu <dxu@dxuuu.xyz> - cmake: don't use language extensions (like gnu++14) (4ce4afc) by Matheus Marchini <mmarchini@netflix.com> - add file extension on README (545901c) by sangyun-han <sangyun628@gmail.com> - build: don't set -std flag manually (3cbc482) by Matheus Marchini <mat@mmarchini.me> - Don't use random value on stack (b67452b) by Daniel Xu <dxu@dxuuu.xyz> - codegen: ensure logical OR and AND works with non-64-bit integers (69cbd85) by Matheus Marchini <mat@mmarchini.me> - Allow child process to exit on attach_probe failure (#868) (ecf1bc8) by Adam Jensen <acjensen@gmail.com> - json output: Make output more consistent (#874) (9d1269b) by Dan Xu <dxu@dxuuu.xyz> - Do not generate extra load for ++/-- for maps/variables (3f79fad) by Jiri Olsa <jolsa@kernel.org> #### Fixed - semantic_analyser: validate use of calls as map keys (b54c085) by Matheus Marchini <mat@mmarchini.me> - codegen: fix rhs type check for binop (2d87213) by Daniel Xu <dxu@dxuuu.xyz> - Fix map field access (a9acf92) by Jiri Olsa <jolsa@kernel.org> - Correctly parse enums (59d0b0d) by Daniel Xu <dxu@dxuuu.xyz> - Allow build from uncommon bcc installation (9986329) by Jiri Olsa <jolsa@kernel.org> - Fix sigint handling under heavy load (0058d41) by Augusto Caringi <acaringi@redhat.com> - Assign default value to elem_type to avoid undefined behavior. (a0b8722) by Florian Kuebler <kuebler@google.com> - Strip trailing newline from error message (5315eee) by bas smit <bas@baslab.org> - Use strerror to improve `cgroupid` error message (72de290) by bas smit <bas@baslab.org> - Initialize member variable (4dd8bb8) by Daniel Xu <dxu@dxuuu.xyz> - Fix umask build issue (#861) (24de62a) by Michael Würtinger <michael@wuertinger.de> - Handle SIGTERM gracefully (#857) (fb47632) by Dan Xu <dxu@dxuuu.xyz> - json output: suppress output if map is not initialized (348975b) by Andreas Gerstmayr <agerstmayr@redhat.com> - fix 'designated initializers' build errors (#847) (4910e75) by Alek P <alek-p@users.noreply.github.com> - remove invalid 'unused attribute' (9bf8204) by Matheus Marchini <mat@mmarchini.me> #### Documentation - Mention sargX builtin in docs (352e983) by Adam Jensen <acjensen@gmail.com> - Update reference guide (65c97fd) by Jiri Olsa <jolsa@kernel.org> - Docs: fix inconsistent install script option (a65e3d8) by Daniel T. Lee <danieltimlee@gmail.com> - docs: Fix mismatch between code and example (2499437) by bas smit <bas@baslab.org> - fix typo in example text - correct name of script (891021b) by sangyun-han <sangyun628@gmail.com> - Add openSUSE package status link into install.md (#859) (613b42f) by James Wang <jnwang@suse.com> - Fix a typo in reference_guide (e7420eb) by James Wang <jnwang@suse.com> - Ubuntu instructions: add minimum release version (413c1a0) by Peter Sanford <psanford@sanford.io> #### Internal - Add tests for sargX builtin (774a7a6) by Adam Jensen <acjensen@gmail.com> - Add test (0c08b1d) by Daniel Xu <dxu@dxuuu.xyz> - Avoid leaking state between cmake tests (625269f) by bas smit <bas@baslab.org> - Avoid testing for FOUR_ARGS_SIGNATURE on systems without bfd (cd1d231) by bas smit <bas@baslab.org> - Unset `CMAKE_REQUIRED_LIBRARIES` to avoid influencing tests (ab0665b) by bas smit <bas@baslab.org> - Define PACKAGE to make libbfd happy (d165396) by Daniel Xu <dxu@dxuuu.xyz> - Fix 'may be used uninitialized' build warning in bfd-disasm.cpp (ffd203b) by Augusto Caringi <acaringi@redhat.com> - Change "variable.tracepoint arg casts in predicates" runtime test (9aae057) by Jiri Olsa <jolsa@kernel.org> - bfd-disasm: fix LIBBFD_DISASM_FOUR_ARGS_SIGNATURE (7d62627) by Matheus Marchini <mmarchini@netflix.com> - semantic_analyser: fix gcc build error on xenial (0e6014a) by Matheus Marchini <mmarchini@netflix.com> - Prevent forks from notifying the IRC channel (ca93440) by Daniel Xu <dxu@dxuuu.xyz> - Add runtime tests for uprobe offset/address (d9c2bab) by Jiri Olsa <jolsa@kernel.org> - Bypass the uprobe align check in unsafe mode (18b9635) by Jiri Olsa <jolsa@kernel.org> - Adding tests for uprobe offset definitions (d894d0e) by Jiri Olsa <jolsa@kernel.org> - Add BfdDisasm class (8198628) by Jiri Olsa <jolsa@kernel.org> - Add Disasm class (6f7bc6f) by Jiri Olsa <jolsa@kernel.org> - Add support to check for libbfd/libopcodes libraries (542f2b9) by Jiri Olsa <jolsa@kernel.org> - Add resolve_offset_uprobe functions (7be4143) by Jiri Olsa <jolsa@kernel.org> - Add address and func_offset to ast::AttachPoint and Probe classes (893201a) by Jiri Olsa <jolsa@kernel.org> - Fix `sigint under heavy load` runtime test (4f7fd67) by Daniel Xu <dxu@dxuuu.xyz> - Notify irc channel on build failures (83b5684) by Daniel Xu <dxu@dxuuu.xyz> - Add BTF class (43530aa) by Jiri Olsa <jolsa@kernel.org> - Read every BTF type (67dbe3f) by Daniel Xu <dxu@dxuuu.xyz> - Disable codegen.logical_and_or_different_type test in alpine CI (5271e6c) by Daniel Xu <dxu@dxuuu.xyz> - Warn when doing signed division (#910) (fff3b05) by Daniel Xu <dxu@dxuuu.xyz> - Add short option for --btf and update usage (88dbe47) by Daniel Xu <dxu@dxuuu.xyz> - Add BTF tests (47621bb) by Jiri Olsa <jolsa@kernel.org> - Add ClangParser::parse_btf_definitions function (54cf4ab) by Jiri Olsa <jolsa@kernel.org> - Add SizedType::operator!= function (8cb79f9) by Jiri Olsa <jolsa@kernel.org> - Add ClangParserHandler::check_diagnostics function (3e75475) by Jiri Olsa <jolsa@kernel.org> - Add ClangParser::visit_children function (4842ccf) by Jiri Olsa <jolsa@kernel.org> - Add BTF::c_def function (02a2d0d) by Jiri Olsa <jolsa@kernel.org> - Add Expression::resolve string set (0779333) by Jiri Olsa <jolsa@kernel.org> - Add curtask task_struct cast type for field access (80cb0d7) by Jiri Olsa <jolsa@kernel.org> - test: fix watchpoint runtime test flakiness (88fc1b8) by Matheus Marchini <mmarchini@netflix.com> - Disable sign checking for division binop (8084463) by bas smit <bas@baslab.org> - Add ability to test for warnings (b19ebb6) by bas smit <bas@baslab.org> - Revert "Signed types (#834)" (6613a14) by Daniel Xu <dxu@dxuuu.xyz> - Signed types (#834) (446facb) by bas smit <bas@baslab.org> - test: fix flaky 32-bit tp runtime test (c0d94c8) by Matheus Marchini <mat@mmarchini.me> - travis: use bionic and enable runtime tests (57c5a55) by Matheus Marchini <mat@mmarchini.me> - test: print bpftrace script when codegen test fails (b0c4902) by Matheus Marchini <mat@mmarchini.me> - tests: add test for cat with fmt str (#842) (b3143a6) by Matheus Marchini <mat@mmarchini.me> - Fix tests (#844) (fd0ec92) by bas smit <bas@baslab.org> ## [0.9.2] 2019-07-31 ### Highlights - New environment variables (BPFTRACE_NO_USER_SYMBOLS, BPFTRACE_LOG_SIZE) - New probe type: memory `watchpoint` - Support for JSON output ### All Changes #### Added - Add vargs support for cat() builtin (similar to system) (7f1aa7b) by Augusto Caringi <acaringi@redhat.com> - Add memory watchpoint probe type (#790) (854cd4b) by Dan Xu <dxu@dxuuu.xyz> - Add support for Go symbol names to uaddr (#805) (e6eb3dd) by Jason Keene <jasonkeene@gmail.com> - add option for JSON output (5c6f20a) by Andreas Gerstmayr <andreas@gerstmayr.me> - Add $# for number of positional arguments (ec8b61a) by Mark Drayton <mdrayton@gmail.com> - Add BPFTRACE_NO_USER_SYMBOLS environment variable (#800) (41d2c9f) by Dan Xu <dxu@dxuuu.xyz> - Add line numbers to parser error messages (a584752, 2233ea7) by bas smit <bas@baslab.org> - Add new environment variable BPFTRACE_LOG_SIZE (2f7dc75, 7de1e84, 2f7dc75) by Ray Jenkins <ray.jenkins@segment.com> #### Changed - Terminate when map creation fails (6936ca6) by bas smit <bas@baslab.org> - Print more descriptive error message on uprobe stat failure (0737ec8) by Dan Xu <dxu@dxuuu.xyz> - Allow '#' in attach point path (2dfbc93) by Dan Xu <dxu@dxuuu.xyz> - Disable `func`, `retval` and `reg` for tracepoints since tracepoints can't access this information (7bfc0f8) by bas smit <bas@baslab.org> #### Fixed - Skip keys which were removed during iteration on `print` (bfd1c07) by Andreas Gerstmayr <agerstmayr@redhat.com> - Fix exiting prematurely on strace attach (a584752..0e97b2c) by Jay Kamat <jaygkamat@gmail.com> - Fix unused variable warnings (9d07eb5) by Daniel Xu <dxu@dxuuu.xyz> - Fix alignment issues on `ntop` (2006424) by Matheus Marchini <mat@mmarchini.me> - Fix BEGIN being triggered multiple times when bpftrace is run a second time (14bc835) by bas smit <bas@baslab.org> - Fix crash when using $0 (b41d66d) by Alastair Robertson <alastair@ajor.co.uk> - Fix tcp tools printing errors (206b36c) by bas smit <bas@baslab.org> #### Documentation - Update Ubuntu install instructions (4e3ffc3) by Brendan Gregg <bgregg@netflix.com> - Clarify help message for `-o` (d6e9478) by Daniel Xu <dxu@dxuuu.xyz> - `opensnoop.bt` was incorrectly linked to load.bt (d74fae0) by southpawflo <16946610+southpawflo@users.noreply.github.com> - Document multiple attach points for probes (21bc5bf) by Daniel Xu <dxu@dxuuu.xyz> - Fix incorrect reference to the `probe` key (83d473c) by Jeremy Baumont <jeremy.baumont@gmail.com> #### Internal - Fix failing test (086c018) by bas smit <bas@baslab.org> - Collapse bcc symbol resolvers by process executable (63ff8b0) by Daniel Xu <dxu@dxuuu.xyz> - Remove unneeded probe read (7d0aa99) by bas smit <bas@baslab.org> - Fix runtime test parser to not break with commented out tests (#824) (b73c963) by Augusto Mecking Caringi <acaringi@redhat.com> - bpftrace: optimize resolve_kname (#765) (ec5278d) by Matheus Marchini <mat@mmarchini.me> - Resolve symbol names using bcc_elf_foreach_sym (#811) (a2d9298) by Jason Keene <jasonkeene@gmail.com> - Add basic editorconfig for defining style (#775) (5b20829) by Jay Kamat <jaygkamat@gmail.com> - Auto-generate list of includes for codegen tests (e3b8ecd) by Alastair Robertson <alastair@ajor.co.uk> - Do not emit GEP instruction when pushing string literals to stack (#667) (e98530c) by Michał Gregorczyk <michalgr@users.noreply.github.com> - tool style tweaks (8bb0940) by Brendan Gregg <bgregg@netflix.com> - Clean up unused variable (#787) (8627e84) by Dan Xu <dxu@dxuuu.xyz> - Make member variables end with underscores (c76a8e4) by Alastair Robertson <alastair@ajor.co.uk> - Fail in case there's unresolved type in definitions (ecb7a1b, 2239756, a6a4fb3) by Jiri Olsa <jolsa@kernel.org> ## [0.9.1] 2019-06-25 ### Highlights - Introduce compound assignment operators (`+=` and friends) (7f26468) by Matheus Marchini <mat@mmarchini.me> - Add support for arrays and IPv6 for the `ntop` builtin function (c9dd10f) by Matheus Marchini <mat@mmarchini.me> - Add basic support to enums (treat them as constants) (e4cb6ce) by Matheus Marchini <mat@mmarchini.me> - Add macro definition support (8826470,af67b56,14e892b) by Matheus Marchini <mat@mmarchini.me>, Javier Honduvilla Coto <javierhonduco@gmail.com> - Add support for arrays and IPv6 for the `ntop` builtin function (c9dd10f) by Matheus Marchini <mat@mmarchini.me> - Allow comparison of two string variables (7c8e8ed) by williangaspar <williangaspar360@gmail.com> - Add pre and post behavior to ++ and -- operators (f2e1345...9fea147) by Alastair Robertson <alastair@ajor.co.uk> - [**BREAKING CHANGE**] Ban kprobes that cause CPU deadlocks (40cf190) by Javier Honduvilla Coto <javierhonduco@gmail.com> - [**BREAKING CHANGE**] Add unsafe-mode and make default execution mode safe-mode (981c3cf,4ce68cd) by Daniel Xu <dxu@dxuuu.xyz> ### All Changes #### Added - Introduce compound assignment operators (`+=` and friends) (7f26468) by Matheus Marchini <mat@mmarchini.me> - Add KBUILD_MODNAME (a540fba) by Brendan Gregg <bgregg@netflix.com> - Add flags for include paths and files (`--include` and `-I`, respectively) (632652f) by Matheus Marchini <mat@mmarchini.me> - List uprobes with -l (122ef6e) by Matheus Marchini <mat@mmarchini.me> - Add BPFTRACE_MAX_PROBES environment variable (ddb79df) by Matheus Marchini <mat@mmarchini.me> - Add option to redirect trace output to file (462a811) by bas smit <bas@baslab.org> - Add script to check kernel requirements (ac19743) by bas smit <bas@baslab.org> - Add USDT wildcard matching support (82dbe4e...3725edf,648a65a) by Dale Hamel <dale.hamel@srvthe.net> - Add support for arrays and IPv6 for the `ntop` builtin function (c9dd10f,24a463f) by Matheus Marchini <mat@mmarchini.me> - Add 'cat' builtin (ae1cfc9,ef9baf8) by Augusto Caringi <acaringi@redhat.com> - Add array indexing operator [] for one-dimensional, constant arrays (ec664a1) by Dale Hamel <dalehamel@users.noreply.github.com> - Allow dots to truncate fields in `printf` (0f636c9) by Brendan Gregg <bgregg@netflix.com> - Add `BPFTRACE_MAP_KEYS_MAX` environment variable, and increase default map keys limit to 4096 (fab8bf6) by Brendan Gregg <bgregg@netflix.com> - Add support for delimiters in join() statement (eb40386) by Jason Koch <jkoch@netflix.com> - Add basic support to enums (treat them as constants) (e4cb6ce) by Matheus Marchini <mat@mmarchini.me> - Add macro definition support (8826470,af67b56,14e892b) by Matheus Marchini <mat@mmarchini.me>, Javier Honduvilla Coto <javierhonduco@gmail.com> - Add hardware:branch-misses (9631623) by Jason Koch <jkoch@netflix.com> - Allow comparison of two string variables (7c8e8ed) by williangaspar <williangaspar360@gmail.com> #### Changed - Add pre and post behavior to ++ and -- operators (f2e1345...9fea147) by Alastair Robertson <alastair@ajor.co.uk> - Parse negative integer literals correctly (108068f) by Daniel Xu <dxu@dxuuu.xyz> - Tools improvements (9dbee04,a189c36) by Brendan Gregg <bgregg@netflix.com> - USAGE message trim (18d63b0) by Brendan Gregg <bgregg@netflix.com> - Allow `probe` builtin for `BEGIN` and `END` probes (3741efe) by bas smit <bas@baslab.org> - Default -d and -dd output to stdout (ecea569) by Jay Kamat <jaygkamat@gmail.com> - Return with error code if clang finds an error while parsing structs/enums/macros/includes (364849d) by Matheus Marchini <mat@mmarchini.me> - Restore map key validation (7826ee3) by Alastair Robertson <alastair@ajor.co.uk> - Add `/usr/include` to default header search path (32dd14b) by Javier Honduvilla Coto <javierhonduco@gmail.com> - More information in error message when failing to open script file (3b06e5f) by Alastair Robertson <alastair@ajor.co.uk> - [**BREAKING CHANGE**] Add unsafe-mode and make default execution mode safe-mode (981c3cf,4ce68cd) by Daniel Xu <dxu@dxuuu.xyz> - Safety measure for LLVM out of memory issue (6b53e4a) by Brendan Gregg <bgregg@netflix.com> - Allow non-zero lhist min value (51fdb6a) by bas smit <bas@baslab.org> - Improvements in startup speed (5ed8717,1ffb50f) by Matheus Marchini <mat@mmarchini.me> - When using -c, spawn the child process only when the tracing is ready (e442e9d) by Jiri Olsa <jolsa@kernel.org> - Allow more pointers as ints (3abc93e) by Brendan Gregg <bgregg@netflix.com> - Validate that PID (received via `-p`) is an integer (48206ad) by Javier Honduvilla Coto <javierhonduco@gmail.com> - Promote map keys to 64-bit (e06e39d) by Brendan Gregg <bgregg@netflix.com> - Add hint when traced PID is not running (9edb3e1) by Javier Honduvilla Coto <javierhonduco@gmail.com> - Allow pointers in printf, mapkeys, and filters (0202412,280f1c6) by Brendan Gregg <bgregg@netflix.com> - Allow ksym() lookups on function pointers (2139d46) by Brendan Gregg <bgregg@netflix.com> - [**BREAKING CHANGE**] Ban kprobes that cause CPU deadlocks (40cf190) by Javier Honduvilla Coto <javierhonduco@gmail.com> #### Fixed - Workaround for asm goto in Kernel 5+ headers (60263e1) by Matheus Marchini <mat@mmarchini.me> - Properly handle invalid `args` utilization (13c2e2e) by Augusto Caringi <acaringi@redhat.com> - Fix abort caused by lhist with incorrect number of arguments (41036b9) by bas smit <bas@baslab.org> - Fix anonymous struct parsing (ea63e8b) by Alastair Robertson <alastair@ajor.co.uk> - Fix code generation for bitwise and logical not on integer values (f522296) by synth0 <synthkaf@outlook.com> - Fix typo in type mismatch error message (83924f8) by Jay Kamat <jaygkamat@gmail.com> - Fix clearing action for some aggregations (dcd657e) by Javier Honduvilla Coto <javierhonduco@gmail.com> - Fix possible crash if an invalid char is used in search (c4c6894) by Augusto Caringi <acaringi@redhat.com> - Fix headers includes by using -isystem rather than -I (32daaa2) by Javier Honduvilla Coto <javierhonduco@gmail.com> - Fix exit() function bypassing END probe processing #228 (f63e1df,e4c418e,5cce746) by Augusto Caringi <acaringi@redhat.com> - Fix order in which probes fire (a4bf870) by John Gallagher <john.gallagher@delphix.com> - Stop throwing 'failed to initialize usdt context for path' error message (1fa3d3c) by Augusto Caringi <acaringi@redhat.com> - Fix stringification of ntop keys in maps (598050e) by Matheus Marchini <mat@mmarchini.me> - Fix parsing of forward-decl structs inside structs (354c919) by Matheus Marchini <mat@mmarchini.me> - Fix struct definition from headers (4564d55) by Matheus Marchini <mat@mmarchini.me> - Avoid crash if incorrect command line option is used (aa24f29) by Augusto Caringi <acaringi@redhat.com> - Fix clang_parser for LLVM 8+ (80ce138) by Matheus Marchini <mat@mmarchini.me> - Fix semicolon being required in some cases after if statements (13de974) by Matheus Marchini <mat@mmarchini.me> - Throw error message if argN or retval is used with incorrect probe type (b40354c) by Augusto Caringi <acaringi@redhat.com> - Fix USDT listing (`-l`) without a search pattern (af01fac) by Javier Honduvilla Coto <javierhonduco@gmail.com> - Add missing space to error message (e1f5f14) by Alastair Robertson <alastair@ajor.co.uk> - Fix unroll in some cases (mostly when the generated code was large) (702145c) by Matheus Marchini <mat@mmarchini.me> #### Documentation - Added info on clang environment variables (7676530) by Richard Elling <Richard.Elling@RichardElling.com> - Fix snap instructions. (3877e46) by George Slavin <george.r.slavin@gmail.com> - Fix ustack documentation (5eeeb10) by Daniel Xu <dxu@dxuuu.xyz> - Replace stack with kstack (49e01e0) by Javier Honduvilla Coto <javierhonduco@gmail.com> - Fix TOC in the reference guide (05eb170) by Alastair Robertson <alastair@ajor.co.uk> - Fix broken links in docs (c215c61,845f9b6) by Daniel Xu <dxu@dxuuu.xyz> - Fix inaccurate tutorial on listing (a4aeaa5) by Daniel Xu <dxu@dxuuu.xyz> - Add documentation for BEGIN/END probes (81de93a) by Daniel Xu <dxu@dxuuu.xyz> - Update build instructions for Ubuntu (38b9620) by bas smit <bas@baslab.org> - INSTALL.md: update required dependency for usdt (5fc438e) by Zi Shen Lim <zlim.lnx@gmail.com> - Fix ++ and -- text on undefined variables (47ab5cd) by Matheus Marchini <mat@mmarchini.me> - Reference guide small fixes (0d9c1a4) by Augusto Caringi <acaringi@redhat.com> - Add instructions to install on Gentoo (3c23187) by Patrick McLean <chutzpah@gentoo.org> - Add install instructions for Ubuntu snap package (0982bb6) by George Slavin <george.r.slavin@gmail.com> - Fix spelling mistake (a45869f) by George Slavin <george.r.slavin@gmail.com> - Fix 'one liners tutorial': use 'openat' instead of 'open' in examples (0cce55c) by Augusto Caringi <acaringi@redhat.com> - Add contributing section to the README (2a08468) by Alastair Robertson <alastair@ajor.co.uk> - Standardise documentation on the bpftrace name (135a4d3) by Alastair Robertson <alastair@ajor.co.uk> - Update install instructions (505b50a) by Alastair Robertson <alastair@ajor.co.uk> #### Internal - [tests] add missing tests to codegen.cpp (012ebda) by Matheus Marchini <mat@mmarchini.me> - tests: add runtime tests for regression bugs (ee57b6f) by Matheus Marchini <mat@mmarchini.me> - vagrant: add Ubuntu 19.04 box (60e6d0a) by Matheus Marchini <mat@mmarchini.me> - docker: add Fedora 30 (9ccafa0) by Zi Shen Lim <zlim.lnx@gmail.com> - Add Vagrantfile for ubuntu (b221f79) by bas smit <bas@baslab.org> - tests: fix and improve runtime tests (c7b3b2f) by Matheus Marchini <mat@mmarchini.me> - Clean up includes in clang_parser (374c240) by Daniel Xu <dxu@dxuuu.xyz> - Remove double `check_nargs` call (c226c10) by bas smit <bas@baslab.org> - Fix call.system runtime test (3b4f578) by Daniel Xu <dxu@dxuuu.xyz> - Fix call.str runtime test (8afbc22) by Daniel Xu <dxu@dxuuu.xyz> - Fix k[ret]probe_order runtime tests (27a334c) by Daniel Xu <dxu@dxuuu.xyz> - Remove old TODO (5be3752) by Alastair Robertson <alastair@ajor.co.uk> - Add clang_parser::parse_fail test (6fd7aac) by Jiri Olsa <jolsa@kernel.org> - Fix some bugs with positional parameters (13fb175) by Alastair Robertson <alastair@ajor.co.uk> - Fix runtime tests (a05ee59) by bas smit <bas@baslab.org> - Enable multiline matching for runtime test regex (c8763e4) by bas smit <bas@baslab.org> - Add environment var support to runtime tests (543513e) by bas smit <bas@baslab.org> - Disable codegen.printf_offsets test for LLVM5 CI build (ea8a7e4) by Alastair Robertson <alastair@ajor.co.uk> - Fix LLVM 5 tests (938e79b) by Alastair Robertson <alastair@ajor.co.uk> - Refactor find_wildcard_matches() to allow for proper testing (371c7cf) by Alastair Robertson <alastair@ajor.co.uk> - tests: Use Python 3 for integration tests + test fix (#651) (4b0e477) by Javier Honduvilla Coto <javierhonduco@gmail.com> - Add --unsafe to more runtime tests (8b2234a) by Daniel Xu <dxu@dxuuu.xyz> - Fix 'ignoring return value' build warning (bdc9f16) by Augusto Caringi <acaringi@redhat.com> - Fix 'signed overflow' related build warning (0ece2a9) by Augusto Caringi <acaringi@redhat.com> - Fix UnboundLocalError on skipped test (03958cb) by John Gallagher <john.gallagher@delphix.com> - Use getopt_long instead of getopt (d732298) by Daniel Xu <dxu@dxuuu.xyz> - Fix docs typo (05bf095) by bas smit <bas@baslab.org> - check explicitly for systemtap sys/sdt.h and ignore if not present (831633d) by Jason Koch <jkoch@netflix.com> - Suppress build warning in GCC >=8 caused by #474 (71d1cd5) by Augusto Caringi <acaringi@redhat.com> - Remove more tabs (e9594dd) by Alastair Robertson <alastair@ajor.co.uk> - Convert tabs to spaces (585e8b5) by Alastair Robertson <alastair@ajor.co.uk> - Add existence tests for kstack, kstack() and ustack() (954d93d) by Alastair Robertson <alastair@ajor.co.uk> - [tests] more runtime tests enhancements (#586) (249c7a1) by Matheus Marchini <mat@mmarchini.me> - Codegen: Fix assigning non-struct "internal" values to maps (4020a5c) by Alastair Robertson <alastair@ajor.co.uk> - Fix typo on LLVM_REQUESTED_VERSION macro in CMakeLists.txt (82dbe4e) by Quentin Monnet <quentin.monnet@netronome.com> - Fix build warning (a77becb) by Alastair Robertson <alastair@ajor.co.uk> - [tests] allow tests to be skipped if a given condition is not met (59fa32a) by Matheus Marchini <mat@mmarchini.me> - [tests] make other.if_compare_and_print_string less flaky (840bbb3) by Matheus Marchini <mat@mmarchini.me> - Fix compile warnings and mark more functions as const (cfb058d) by Alastair Robertson <alastair@ajor.co.uk> - Misc readability fixes (9581e01) by Fangrui Song <i@maskray.me> - build: unify dockerfiles under a bionic image (445fb61) by Matheus Marchini <mat@mmarchini.me> - [tests] fix and enhance runtime tests (ea5deb9) by Matheus Marchini <mat@mmarchini.me> - [tests] add test script to run tools with -d (4ff113d) by Matheus Marchini <mat@mmarchini.me> - [clang_parser] decouple kernel cflags from the parser method (ad753d5) by Matheus Marchini <mat@mmarchini.me> - Address TODO items related to objdump dependency (382b9b7) by Adam Jensen <acjensen@gmail.com> - Fall back to objdump/grep if bcc is older (fdd02ec) by Adam Jensen <acjensen@gmail.com> - [clang_parser] pass BPFtrace as arg instead of StructMap (a0af75f) by Matheus Marchini <mat@mmarchini.me> - [ast] introduce Identifier type to AST (389d55f) by Matheus Marchini <mat@mmarchini.me> - use CMAKE_SYSTEM_PROCESSOR when selecting whether to include x86_64 or aarch64 sources (0ea7a63) by Michał Gregorczyk <michalgr@fb.com> - Clearify error message for mismatched llvm. (9b77fee) by George Slavin <george.r.slavin@gmail.com> - Add more info to LLVM mismatch error message (1e3b1be) by George Slavin <george.r.slavin@gmail.com> - Allow 0 as kernel version during first attempt to call bcc_prog_load (13499ac) by Michał Gregorczyk <michalgr@fb.com> - Fix bpftrace_VERSION_MINOR in CMakeLists.txt (8 -> 9) (13321eb) by Matheus Marchini <mat@mmarchini.me> - Fix version information when not building inside a git repo (#489) (1f33126) by Augusto Caringi <acaringi@redhat.com> - Do not try to load bpf program with unknown kernel version (2c00b7f) by Michał Gregorczyk <michalgr@fb.com> - Add better checks for llvm version (4fe081e) by George Slavin <george.r.slavin@gmail.com> - Fix deprecated stack warning in builtin_stack test (a1aaed8) by George Slavin <george.r.slavin@gmail.com> - add test for 32-bit tp args (77f7cb7) by Brendan Gregg <bgregg@netflix.com> - tests: add some basic integration tests (e9805af) by Javier Honduvilla Coto <javierhonduco@gmail.com> - Fix and simplify lexer.l (57bae63) by Fangrui Song <i@maskray.me> - Fix 2 clang warnings: -Wmismatched-tags and -Wpessimizing-move (18da040) by Fangrui Song <i@maskray.me> - Revert "Stop linking against bcc-loader-static" (5b6352c) by Alastair Robertson <alastair@ajor.co.uk> - fix typo on BPF_FUNC_get_current_cgroup_id missing message (27371c3) by Jason Koch <jkoch@netflix.com> - propagate HAVE_GET_CURRENT_CGROUP_ID to ast modules (57e30da) by Jason Koch <jkoch@netflix.com> - Add missing include (5763dc2) by Michał Gregorczyk <michalgr@fb.com> - No need for `if` when we're not doing anything (a65ad14) by Alastair Robertson <alastair@ajor.co.uk> - Make indirect* related data static (24d9dd2) by Jiri Olsa <jolsa@kernel.org> - Fix issues, add tests and improve reliability of positional parameters (acec163,f2e1345) by Matheus Marchini <mat@mmarchini.me> ## [0.9.0] 2019-03-16 ### Deprecated - **Deprecate `sym()`**. Use `ksym()` instead (50a66d2) by williangaspar - **Deprecate `stack`**. Use `kstack` instead (e8b99cd) by williangaspar ### Added - List usdt probes with -l (fa7d5f3) by Timothy J Fontaine - Introduce perf formatting for ustack()/kstack() (db910b9) by Matheus Marchini - Add increment and decrement operators (++/--) (c8d8a08, 6aa66a1, 223d8d8, 1f82aaf, 8c5c4ea) by Dale Hamel - Add changelog file to keep track of unreleased changes (d11fb01) by Matheus Marchini - Allow args with multiple tracepoints (a0a905f, 2df50d3, cddae1a) by Brendan Gregg - Add elapsed builtin (0fde181) by Brendan Gregg - Add support to demangle C++ symbols in userspace stack traces (872525c) by Augusto Caringi - allow \r (e7f0584) by Brendan Gregg - Use debuginfo files information when available (1132d42) by Augusto Caringi - Add ustack([int limit])/kstack([int limit]) calls (08da997) by Matheus Marchini - Allow custom provider name in USDT probe definition (361245c, 80d640a, 20ddfed, c3a6ff1) by Dale Hamel - Detect kernel headers even if they are splitted into source/ and build/ directories (4d76385) by Kirill Smelkov - Add support for arm64 (aarch64) (47fa8aa) by Ali Saidi - Allow customizing stdout buffering mode via -b (1663b84) by Hongli Lai (Phusion) - Add support to list tracepoint arguments (#323) (4a048fc) by Augusto Caringi - Add `ksym` as a replacement for `sym` (50a66d2) by williangaspar - Add `kstack` as a replacement for `stack` (e8b99cd, 840712b, f8f7ceb,6ec9a02) by williangaspar - cmake: add BUILD_TESTING support (a56ab12) by Aleksa Sarai - Add --version (61a4650, eab3675) by williangaspar - Add hint to install docs and normalize format (c0084a2) by Daniel Xu - Make bpftrace -l list sofware and hardware types (#44) (acd9a80) by Augusto Caringi - Print program ID when the verbose option is enabled. (8e8258d) by David Calavera ### Changed - Use `struct` when casting on docs and tools (e2ba048) by Brendan Gregg - Allow using the `struct` keyword when casting (df03256) by williangaspar - Make path optional on usdts when attaching to pid (c1c7c83) by Timothy J Fontaine - Resolve binary name from PATH for usdts and uprobes (28f0834) by Matheus Marchini - Use map lookups instead of sequential checks in tcpdrop.bt and tcpretrans.bt (cb0969c) by Slavomir Kaslev - Implicitly declare variables to 0 if used but not defined (a408cc2) by Matheus Marchini - Sort all integer maps by values, ascending (c378f57) by Dale Hamel - Change Ubuntu install to LLVM 6.0 (98353bf) by Brendan Gregg - ignore EFAULT stack IDs (f080bbf) by Brendan Gregg - Usage updates (6de4101) by Brendan Gregg - make map stack indentation 4 chars (c1dd418) by Brendan Gregg - Print error messages on all `abort()` calls (5c2ca5b) by williangaspar - Lesson 9: Replace "stack" to "kstack" (1ac56bd) by CavemanWork - Use structs with semicolons in tools and documentation (85dba93) by Brendan Gregg - Allow semicolon after struct definition (5982c74) by williangaspar - remove unnecessary newlines in -l (bb4a83c) by Brendan Gregg - list sw/hw probes with full names (6f3e1c4) by Brendan Gregg - hist: split negative, zero, and one into separate buckets (48c0afb) by Brendan Gregg - lhist: interval notation tweak (43e7974) by Brendan Gregg - runqlat.bt: remove if semicolon (c10c0dc) by Brendan Gregg - Probe list optimizations and improvements (7f84552) by Augusto Caringi - Link against system installed bcc (#327) (4c3fbad) by Dan Xu - Make semicolon optional after if and unroll blocks (d74d403) by williangaspar - Avoid crashing if mistakenly just '-d' or '-v' is used (f2f6732) by Augusto Caringi - Return cleanly after printing help (1d41717) by Daniel Xu ### Fixed - Make sure we create map keys when we have all the typing information (971bd77) by Matheus Marchini - Fix for new bpf_attach_kprobe signature (080bef8) by Matheus Marchini - Fix string comparison improperly deallocating variables (ffa173a) by williangaspar - Fix probe keys on maps when the map is used more than one time (df81736) by Matheus Marchini - Fix using same variable name on multiple programs (61a14f2) by williangaspar - Fix build on old compilers (644943a, 1b69272) by Kirill Smelkov - Fix build with latest bcc (d64b36a) by williangaspar - Don't throw warning for undefined types in tracepoint structure definition if `args` is not used (f2ebe1a) by Matheus Marchini - Fix for 'redefinition of tracepoint' warning message (baaeade) by Augusto Caringi - Minor fixes in our documentation (0667533) by Matheus Marchini - Fix string comparison (5e114dd, 63acdb6) by williangaspar - Prevent empty trigger functions to be optimized away with -O2 (#218) (9f2069b) by Augusto Caringi - Fix -l behavior with shortcut probe names (2d30e31) by williangaspar - Fix alpine docker build (#372) (2b83b67) by Dan Xu - Fix tracepoint wildcards (946c785) by Brendan Gregg - tests: fix codegen test fot call_hist (342fd6d) by Matheus Marchini - docs: fix trivial typos (3da1980) by Xiaozhou Liu - Fix symbol translation for func, sym, and stack (6276fb5) by Brendan Gregg - Fix wrong package name in Ubuntu Dockerfile (f8e67a9) by xbe - Fix wrong package name in build instructions (8e597de) by Daniel Xu - Fix arguments and error messages for tracepoint shortcut `t` (0eddba7) by williangaspar ### Internal - Fix 'different signedness' warning messages in codegen call_[uk]stack.cpp (cb25318) by Augusto Caringi - Fix 'signedness' warning message in tracepoint_format_parser.cpp (c3e562f) by Augusto Caringi - Stop linking against bcc-loader-static (5fbb7a7) by Daniel Xu - Speeding up runtime tests (60c5d96) by williangaspar - docker: make sure debugfs is mounted (7dcfc47) by Zi Shen Lim - Better coverage for variable_clear() (34fdded) by williangaspar - Add missing space (c65e7c1) by puyuegang - Ignore warnings on code generated by bison (a935942) by Matheus Marchini - Ignore warnings from LLVM headers (b6c4fd6) by Matheus Marchini - Downgrade back to c++14 (f6986d8) by Matheus Marchini - Fix 'parameter not used' warning (2401ab3) by Matheus Marchini - Fix new build warning msg after c++17 was enabled (e4cbe48) by Augusto Caringi - Get rid of cmake CMP0075 policy warning (9b8208a) by Augusto Caringi - Use C++17 instead of C++14 (4b4d5dc) by Alex Birch - Re-enable more build warnings, fix related warnings #316 (8c383dc) by Augusto Caringi - Define `__BPF_TRACING__` before building (required for kernel 4.19+) (e0bf01d) by Kirill Smelkov - Re-enable subset of build warnings and fix some related warnings #316 (f0f56b0) by Augusto Caringi - Cleanup enforce_infinite_rmlimits : removed getrlimit() : Added error description using strerror() (d76465f) by T K Sourab - use the new libbcc API: bcc_{create_map, prog_load} when possible (c03c39f) by Xiaozhou Liu - resources: generate c++ file instead of c file (5e1350b) by Matheus Marchini - docker: disable runtime tests on CI (0667b92) by Matheus Marchini - Hide -inl.h header from interface (10a43d0) by Daniel Xu ## [0.8.0] - 2019-01-06 This is a release to aid packaging. bpftrace has not reached a 1.0 release status yet, as there are still development changes and things to fix. But what is here should be tremendously useful, provided you bear in mind that there will be some changes made to the programming language and command line options between now and a 1.0 release, so any tools or documentation written will become out of date and require changes. If you are anxiously waiting a 1.0 release, please consider contributing so that it can be released sooner. bpftrace-0.23.2/CMakeLists.txt000066400000000000000000000175511477746507000161560ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.14) project(bpftrace) cmake_policy(SET CMP0057 NEW) # bpftrace version number components. set(bpftrace_VERSION_MAJOR 0) set(bpftrace_VERSION_MINOR 23) set(bpftrace_VERSION_PATCH 2) include(GNUInstallDirs) set(WARNINGS_AS_ERRORS OFF CACHE BOOL "Build with -Werror") set(STATIC_LINKING OFF CACHE BOOL "Build bpftrace as a statically linked executable") set(BUILD_ASAN OFF CACHE BOOL "Build bpftrace with -fsanitize=address") set(ENABLE_MAN ON CACHE BOOL "Build man pages") set(BUILD_TESTING ON CACHE BOOL "Build test suite") set(ENABLE_TEST_VALIDATE_CODEGEN ON CACHE BOOL "Run LLVM IR validation tests") set(BUILD_FUZZ OFF CACHE BOOL "Build bpftrace for fuzzing") set(USE_LIBFUZZER OFF CACHE BOOL "Use libfuzzer for fuzzing") set(FUZZ_TARGET "codegen" CACHE STRING "Fuzzing target") set(ENABLE_SYSTEMD OFF CACHE BOOL "Enable systemd integration") set(KERNEL_HEADERS_DIR "" CACHE PATH "Hard-code kernel headers directory") set(SYSTEM_INCLUDE_PATHS "auto" CACHE STRING "Hard-code system include paths (colon separated, the default value \"auto\" queries clang at runtime)") set(ENABLE_SKB_OUTPUT ON CACHE BOOL "Enable skb_output, will include libpcap") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set (CMAKE_CXX_STANDARD 20) set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_EXTENSIONS OFF) add_compile_options("-Wall") add_compile_options("-Wextra") add_compile_options("-Werror=missing-field-initializers") add_compile_options("-Werror=undef") add_compile_options("-Wpointer-arith") add_compile_options("-Wcast-align") add_compile_options("-Wwrite-strings") add_compile_options("-Wcast-qual") #add_compile_options("-Wconversion") add_compile_options("-Wunreachable-code") #add_compile_options("-Wformat=2") add_compile_options("-Wdisabled-optimization") if (WARNINGS_AS_ERRORS) add_compile_options("-Werror") endif() # Clang compiler produces narrowing errors when calling BPF_LD_MAP_FD in the bcc library # Turning off them before bcc library fixes this if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_compile_options("-Wno-narrowing") endif() include_directories(${CMAKE_SOURCE_DIR}/src) include_directories(${CMAKE_BINARY_DIR}) # Ninja buffers output so gcc/clang think it's not an interactive session. # Colors are useful for compiler errors so force the color if("${CMAKE_GENERATOR}" STREQUAL "Ninja") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") add_compile_options(-fdiagnostics-color=always) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_compile_options(-fcolor-diagnostics) endif() endif() include(CTest) if(STATIC_LINKING) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") set(CMAKE_LINK_SEARCH_START_STATIC TRUE) set(CMAKE_LINK_SEARCH_END_STATIC TRUE) endif(STATIC_LINKING) set_property( GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE ) include_directories(SYSTEM ${KERNEL_INCLUDE_DIRS}) find_package(ZLIB REQUIRED) include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS}) find_package(LibBcc REQUIRED) include_directories(SYSTEM ${LIBBCC_INCLUDE_DIRS}) find_package(LibBpf REQUIRED) include_directories(SYSTEM ${LIBBPF_INCLUDE_DIRS}) if("${LIBBPF_VERSION_MAJOR}.${LIBBPF_VERSION_MINOR}" VERSION_LESS 1.5) message(SEND_ERROR "bpftrace requires libbpf 1.5 or greater") endif() find_package(LibElf REQUIRED) include_directories(SYSTEM ${LIBELF_INCLUDE_DIRS}) find_package(LibCereal REQUIRED) include_directories(SYSTEM ${LIBCEREAL_INCLUDE_DIRS}) find_package(BISON REQUIRED) find_package(FLEX REQUIRED) # `parser_class_name` is deprecated and generates warnings in bison >= 3.3. # But `api.parser.class` is not supported in bison < 3.3. So we must inject # the %define based on the bison version here. if(${BISON_VERSION} VERSION_GREATER_EQUAL 3.3) set(BISON_FLAGS "-Dapi.parser.class={Parser}") else() set(BISON_FLAGS "-Dparser_class_name={Parser}") endif() bison_target(bison_parser src/parser.yy ${CMAKE_BINARY_DIR}/parser.tab.cc COMPILE_FLAGS ${BISON_FLAGS} VERBOSE) flex_target(flex_lexer src/lexer.l ${CMAKE_BINARY_DIR}/lex.yy.cc) add_flex_bison_dependency(flex_lexer bison_parser) add_library(parser STATIC ${BISON_bison_parser_OUTPUTS} ${FLEX_flex_lexer_OUTPUTS}) target_compile_options(parser PRIVATE "-w") target_include_directories(parser PRIVATE src src/ast) include(CheckSymbolExists) set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) check_symbol_exists(name_to_handle_at "sys/types.h;sys/stat.h;fcntl.h" HAVE_NAME_TO_HANDLE_AT) set(CMAKE_REQUIRED_DEFINITIONS) find_package(LibBfd) find_package(LibOpcodes) if(ENABLE_SKB_OUTPUT) find_package(LibPcap) endif() if(ENABLE_SYSTEMD) find_package(PkgConfig) pkg_check_modules(libsystemd REQUIRED IMPORTED_TARGET libsystemd) endif() find_package(LibBlazesym) if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif() if(${LIBBFD_FOUND} AND ${LIBOPCODES_FOUND}) set(HAVE_BFD_DISASM TRUE) endif() include(CheckIncludeFile) check_include_file("sys/sdt.h" HAVE_SYSTEMTAP_SYS_SDT_H) # Some users have multiple versions of llvm installed and would like to specify # a specific llvm version. if(${LLVM_REQUESTED_VERSION}) find_package(LLVM ${LLVM_REQUESTED_VERSION} REQUIRED) else() find_package(LLVM REQUIRED) endif() set(MIN_LLVM_MAJOR 16) if(CMAKE_BUILD_TYPE STREQUAL "Debug") # We assume bpftrace is not being packaged when CMAKE_BUILD_TYPE=Debug. # So allow building with any LLVM version. This is purely for developers. # Packagers are highly discouraged from shipping bpftrace with untested LLVM # releases. set(MAX_LLVM_MAJOR 999) else() set(MAX_LLVM_MAJOR 20) endif() 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}") message(SEND_ERROR "Only versions between ${MIN_LLVM_MAJOR} and ${MAX_LLVM_MAJOR} are supported") message(SEND_ERROR "Specify an LLVM major version using LLVM_REQUESTED_VERSION=") endif() message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}: ${LLVM_CMAKE_DIR}") include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) find_package(Clang REQUIRED) include_directories(SYSTEM ${CLANG_INCLUDE_DIRS}) find_package(LLDB) # BPFtrace compile definitions set(BPFTRACE_FLAGS) if(HAVE_NAME_TO_HANDLE_AT) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_NAME_TO_HANDLE_AT=1) endif(HAVE_NAME_TO_HANDLE_AT) if(HAVE_BFD_DISASM) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BFD_DISASM) if(LIBBFD_DISASM_FOUR_ARGS_SIGNATURE) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" LIBBFD_DISASM_FOUR_ARGS_SIGNATURE) endif(LIBBFD_DISASM_FOUR_ARGS_SIGNATURE) if(LIBBFD_INIT_DISASM_INFO_FOUR_ARGS_SIGNATURE) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" LIBBFD_INIT_DISASM_INFO_FOUR_ARGS_SIGNATURE) endif(LIBBFD_INIT_DISASM_INFO_FOUR_ARGS_SIGNATURE) endif(HAVE_BFD_DISASM) if (LLDB_FOUND) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBLLDB) endif () if(LIBPCAP_FOUND) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBPCAP) endif(LIBPCAP_FOUND) if (HAVE_LIBBPF_UPROBE_MULTI) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBBPF_UPROBE_MULTI) endif(HAVE_LIBBPF_UPROBE_MULTI) if(ENABLE_SYSTEMD) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_LIBSYSTEMD) endif() if(LIBBLAZESYM_FOUND) set(BPFTRACE_FLAGS "${BPFTRACE_FLAGS}" HAVE_BLAZESYM) endif() add_subdirectory(src) if (BUILD_TESTING) add_subdirectory(tests) endif() add_subdirectory(tools) if (ENABLE_MAN) add_subdirectory(man) endif(ENABLE_MAN) set(BASH_COMPLETION_PATH ${CMAKE_CURRENT_SOURCE_DIR}/scripts/bash-completion/bpftrace) install(FILES ${BASH_COMPLETION_PATH} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions) if(NOT TARGET uninstall) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CmakeUninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/CmakeUninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/CmakeUninstall.cmake) endif() bpftrace-0.23.2/CONTRIBUTING-TOOLS.md000066400000000000000000000011611477746507000165330ustar00rootroot00000000000000# Contributing bpftrace tools All new tools must be submitted to the [user-tools repository](https://github.com/bpftrace/user-tools/blob/master/CONTRIBUTING.md) (not here) where they have a chance to develop maturity and collect real world use cases. The tools that exist in this repository are a small collection curated by the bpftrace maintainers that have been battle-tested and are packaged with bpftrace. They are also used for testing and validation. A list of requirements, which is currently under development, for how tools in the user-tools repository get added to the curated list here will be available soon. bpftrace-0.23.2/CONTRIBUTING.md000066400000000000000000000101351477746507000156360ustar00rootroot00000000000000# Contributing Contributions are welcome! Please see the [development section](#development) below for more information. For new bpftrace tools, please add them to the new [user-tools repository](https://github.com/bpftrace/user-tools/blob/master/CONTRIBUTING.md). The tools that exist in this repository are a small collection curated by the bpftrace maintainers. * Bug reports and feature requests: [Issue Tracker](https://github.com/bpftrace/bpftrace/issues) * Development IRC: #bpftrace at irc.oftc.net * [Good first issues](https://github.com/bpftrace/bpftrace/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) ## Development * [Design Principles](docs/design_principles.md) * [Coding Guidelines](docs/coding_guidelines.md) * [RFC Process](#rfc-process) * [Development Guide](docs/developers.md) * [Development Roadmap](https://docs.google.com/document/d/17729Rlyo1xzlJObzHpFLDzeCVgvwRh0ktAmMEJLK-EU/edit) * [Fuzzing](docs/fuzzing.md) * [Nix](docs/nix.md) * [Release Process](docs/release_process.md) * [Tests](tests/README.md) * [DCO](#developers-certificate-of-origin) ## RFC Process This is for "substantial" or breaking changes. Bug fixes, doc updates, small features, and issues tagged with "good first issue" can utilize the normal github pull request workflow. 1. Create a new issue, where the title is prefixed with "RFC" and apply the RFC tag ([example](https://github.com/bpftrace/bpftrace/issues/2954)). Be sure to include the goal(s) of this change, potential downsides (if applicable), and other solutions you've considered. This issue is where discussions can be had about the overall design and approach. For the most part implementation details should NOT be discussed as they often lead to bike-shedding of the overall proposal. 1. If there is either positive signal from one or more of the maintainers or a lack of negative signal feel free to create a POC and, when you're ready, submit a pull request (linking to the original RFC). This is a good place to have others experiment with your POC and discuss implementation details. - It's entirely possible that this POC exposes underlying issues in the original, approved RFC. That's OK! Return to the original RFC and explain why the approved solution does not work or needs adjustment. If the changes are significant enough, the maintainers might ask for an additional approval of the new approach on the RFC. It's also possible that the RFC then gets rejected; these things happen and they are a normal part of the development process. - Depending on the change, you might be asked to gate this behind a "config flag". Meaning that users have to explicitly opt-in to this feature via the addition of a config flag in their script e.g. ```config = { experimental_my_feature=true }``` This allows us to increase development velocity and wait for more user feedback without permanently adding it to bpftrace. Now, it is possible that this feature still may end up getting removed and not added to the language due to user issues or changes in design direction. However, there will be plenty of communication between the maintainers and the original author if this is the case. 1. If the POC is approved by two or more maintainers, please follow the current pull request checklist: - add it to the CHANGELOG - update the adoc - ensure there are unit tests, runtime tests, and codegen tests **Note**: ## Developer's Certificate of Origin To improve tracking of who did what we’ve introduced a “sign-off†procedure. The sign-off is a simple line at the end of every commit, which certifies that you wrote it or otherwise have the right to pass it on as an open-source contribution. The rules are pretty simple: [Developer's Certificate of Origin](https://developercertificate.org/). If you can certify those rules then sign-off all commits with the `--signoff` option provided by `git commit`. For example: ``` git commit --signoff --message "This is the commit message" ``` This option adds a `Signed-off-by` trailer at the end of the commit log message. Please use your real name and an actual email address (sorry, no anonymous contributions, github logins, etc.). bpftrace-0.23.2/INSTALL.md000066400000000000000000000237631477746507000150500ustar00rootroot00000000000000# bpftrace Install - [Linux Kernel Requirements](#linux-kernel-requirements) - [Kernel headers install](#kernel-headers-install) - [Package install](#package-install) - [Ubuntu](#ubuntu-packages) - [Fedora](#fedora-package) - [Gentoo](#gentoo-package) - [Debian](#debian-package) - [openSUSE](#openSUSE-package) - [CentOS](#CentOS-package) - [Arch](#arch-package) - [Alpine](#alpine-package) - [AppImage install](#appimage-install) - [Building bpftrace](#building-bpftrace) - [Ubuntu](#ubuntu) - [Fedora](#fedora) - [Debian](#debian) - [Amazon Linux](#amazon-linux) - (*please add sections for other OSes)* - [Generic build](#generic-build-process) - [Disable Lockdown](#disable-lockdown) # Linux Kernel Requirements For minimum kernel version, see our [dependency support policy](docs/dependency_support.md#linux-kernel). Your kernel needs to be built with the following options: ``` CONFIG_BPF=y CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT=y CONFIG_HAVE_EBPF_JIT=y CONFIG_BPF_EVENTS=y CONFIG_FTRACE_SYSCALLS=y CONFIG_FUNCTION_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_DYNAMIC_FTRACE=y CONFIG_HAVE_KPROBES=y CONFIG_KPROBES=y CONFIG_KPROBE_EVENTS=y CONFIG_ARCH_SUPPORTS_UPROBES=y CONFIG_UPROBES=y CONFIG_UPROBE_EVENTS=y CONFIG_DEBUG_FS=y ``` This can be verified by running the `check_kernel_features` script from the `scripts` directory. # Kernel headers install Usually kernels headers can be installed from a system package manager. In some cases though, this may not be an option, and headers aren't easily available. For instance, the default `docker desktop` (as of writing ships with kernel 4.19 which supports bpf), benefits from this, as does Chromium OS and other lightweight Linux distributions. Newer kernels may have the IKHEADERS option, or support btf - in which case there is no need to build these headers as the kernel provides this. For older kernels, and on distributions where headers may not be available, this script provides a generic means to get bpftrace kernel headers: ```bash #!/bin/bash set -e KERNEL_VERSION="${KERNEL_VERSION:-$(uname -r)}" kernel_version="$(echo "${KERNEL_VERSION}" | awk -vFS=- '{ print $1 }')" major_version="$(echo "${KERNEL_VERSION}" | awk -vFS=. '{ print $1 }')" apt-get install -y build-essential bc curl flex bison libelf-dev mkdir -p /usr/src/linux curl -sL "https://www.kernel.org/pub/linux/kernel/v${major_version}.x/linux-$kernel_version.tar.gz" \ | tar --strip-components=1 -xzf - -C /usr/src/linux cd /usr/src/linux zcat /proc/config.gz > .config make ARCH=x86 oldconfig make ARCH=x86 prepare mkdir -p /lib/modules/$(uname -r) ln -sf /usr/src/linux /lib/modules/$(uname -r)/source ln -sf /usr/src/linux /lib/modules/$(uname -r)/build ``` # Package install ## Ubuntu packages ``` sudo apt-get install -y bpftrace ``` Should work on Ubuntu 19.04 and later. ## Fedora package For Fedora 28 (and later), bpftrace is already included in the official repo. Just install the package with dnf. ``` sudo dnf install -y bpftrace ``` ## Gentoo package On Gentoo, bpftrace is included in the official repo. The package can be installed with emerge. ``` sudo emerge -av bpftrace ``` ## Debian package Is available and tracked [here](https://tracker.debian.org/pkg/bpftrace). ## openSUSE package Is available and tracked [here](https://software.opensuse.org/package/bpftrace). ## CentOS package 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 ``` # AppImage install [AppImages](https://appimage.org/) are a portable way to distribute Linux applications. To the user, they are functionally equivalent to statically linked binaries. bpftrace currently ships AppImages in two locations: * in artifacts on official releases * as a CI artifact for every build on master To download the official release artifacts, see the [latest release](https://github.com/bpftrace/bpftrace/releases/latest). To download the bleeding edge AppImage, go to the [workflow page](https://github.com/bpftrace/bpftrace/actions/workflows/binary.yml) and select the latest run. You should find an uploaded artifact(s) like below: Note that Github will automatically place all build artifacts in a .zip (it's out of our control) so remember to unzip it first. # Building bpftrace ## Ubuntu Due to the kernel requirements Ubuntu 18.04 or newer is highly recommended. ### 18.04 and 18.10 The versions of `bcc` currently available in Ubuntu 18.04 (Bionic) and 18.10 (Cosmic) do not have all the requirements for building `bpftrace` so building `bcc` first is required. The instructions for building `bcc` can be found [here](https://github.com/iovisor/bcc/blob/master/INSTALL.md#install-and-compile-bcc). The build dependencies listed below are also required for `bcc` so install those first. Make sure `bcc` works by testing some of the shipped tools before proceeding. It might be required to `ldconfig` to update the linker. ### 19.04 and newer For 19.04 and newer, please see the [regularly exercised Dockerfile](./docker/Dockerfile.ubuntu) for documentation on how to build bpftrace on Ubuntu. ## Fedora You'll want the newest kernel possible (see kernel requirements), eg, by using Fedora 28 or newer. Please see the [regularly exercised Dockerfile](./docker/Dockerfile.fedora) for documentation on how to build bpftrace on Fedora. ## Debian Please see the [regularly exercised Dockerfile](./docker/Dockerfile.debian) for documentation on how to build bpftrace on Debian. ## Amazon Linux In the future the install should be `yum install bpftrace`. Right now (16-Oct-2018), however, three dependencies need updating in the Amazon Linux repositories (llvm, libtinfo, bison), and bpftrace itself needs to be packaged. The current workaround is to build the three dependencies manually, as well as bpftrace. It's not fun, but it is doable, and will only get better as Amazon updates things. ``` sudo bash builddir=/media/ephemeral0 # change to suit your system: needs about 2 Gbytes free # dependencies yum install git cmake3 gcc64-c++.x86_64 bison flex # llvm cd $builddir wget http://releases.llvm.org/6.0.0/clang+llvm-6.0.0-x86_64-linux-gnu-Fedora27.tar.xz tar xf clang* (cd clang* && sudo cp -R * /usr/local/) cp -p /usr/lib64/llvm6.0/lib/libLLVM-6.0.so /usr/lib64/libLLVM.so # libtinfo.so.6 (comes from ncurses) cd $builddir wget ftp://ftp.gnu.org/gnu/ncurses/ncurses-6.0.tar.gz tar xvf ncurses-6.0.tar.gz cd ncurses-6.0 ./configure --with-shared --with-termlib make -j8 make install # bison cd $builddir wget http://ftp.gnu.org/gnu/bison/bison-3.1.tar.gz tar xf bison* cd bison* ./configure make -j4 make install # bpftrace cd $builddir git clone https://github.com/bpftrace/bpftrace cd bpftrace mkdir build; cd build cmake3 .. make -j8 make install echo /usr/local/lib >> /etc/ld.so.conf ldconfig -v ``` The bpftrace binary will be in installed in /usr/local/bin/bpftrace, and tools in /usr/local/share/bpftrace/tools. You may need to add /usr/local/bin to your $PATH. You can also change the install location using an argument to cmake, where the default is `-DCMAKE_INSTALL_PREFIX=/usr/local`. ## Generic build process Use specific OS build sections listed earlier if available. ### Requirements - A C++ compiler - Libbpf - Libbcc - CMake - Flex - Bison - Asciidoctor - LLVM, LLDB & Clang 10.0+ development packages - LibElf - Binutils development package - Libcereal - Kernel requirements described earlier - Libpcap - Systemtap SDT headers - Zlib development package ### Compilation ``` git clone https://github.com/bpftrace/bpftrace mkdir -p bpftrace/build cd bpftrace/build cmake -DCMAKE_BUILD_TYPE=Release ../ make sudo make install ``` A debug build of bpftrace can be set up with `cmake -DCMAKE_BUILD_TYPE=Debug ../`. The bpftrace binary will be in installed in /usr/local/bin/bpftrace, and tools in /usr/local/share/bpftrace/tools. You can change the install location using an argument to cmake, where the default is `-DCMAKE_INSTALL_PREFIX=/usr/local`. To test that the build works, you can try running the unit tests and a one-liner: ``` $ ./tests/bpftrace_test # ./src/bpftrace -e 'kprobe:do_nanosleep { printf("sleep by %s\n", comm); }' ``` # Disable Lockdown From the original patch set description: > This patchset introduces an optional kernel lockdown feature, intended > to strengthen the boundary between UID 0 and the kernel. When enabled, > various pieces of kernel functionality are restricted. Applications that > rely on low-level access to either hardware or the kernel may cease > working as a result - therefore this should not be enabled without > appropriate evaluation beforehand. > > The majority of mainstream distributions have been carrying variants of > this patchset for many years now, so there's value in providing a > doesn't meet every distribution requirement, but gets us much closer to > not requiring external patches. > > - https://patchwork.kernel.org/patch/11140085/ When lockdown is enabled and set to 'confidentiality' all methods that can extract confidential data from the kernel are blocked. This means that: - kprobes are blocked - tracefs access is blocked - probe_read and probe_read_str are blocked which makes it impossible for bpftrace to function. There are a few ways to disable lockdown. 1. Disable secure boot in UEFI. 2. Disable validation using mokutil, run the following command, reboot and follow the prompt. ``` $ sudo mokutil --disable-validation ``` 3. Use the `SysRQ+x` key combination to temporarily lift lockdown (until next boot) Note that you may encounter kernel lockdown error if you install bpftrace via `snap` incorrectly. Please refer to [Ubuntu](#ubuntu-packages) for more details regrading how to use `snap` to install `bpftrace`. bpftrace-0.23.2/LICENSE000066400000000000000000000261361477746507000144220ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. bpftrace-0.23.2/README.md000066400000000000000000000107021477746507000146640ustar00rootroot00000000000000# bpftrace [![Build Status](https://github.com/bpftrace/bpftrace/workflows/CI/badge.svg?branch=master)](https://github.com/bpftrace/bpftrace/actions?query=workflow%3ACI+branch%3Amaster) [![IRC#bpftrace](https://img.shields.io/badge/IRC-bpftrace-blue.svg)](https://webchat.oftc.net/?channels=bpftrace) [![CodeQL](https://github.com/bpftrace/bpftrace/actions/workflows/codeql.yml/badge.svg)](https://github.com/bpftrace/bpftrace/actions/workflows/codeql.yml) bpftrace is a high-level tracing language for Linux. bpftrace uses LLVM as a backend to compile scripts to [eBPF](https://ebpf.io/what-is-ebpf/)-bytecode and makes use of [libbpf](https://github.com/libbpf/libbpf) and [bcc](https://github.com/iovisor/bcc) for interacting with the Linux BPF subsystem, as well as existing Linux tracing capabilities: kernel dynamic tracing (kprobes), user-level dynamic tracing (uprobes), tracepoints, etc. The bpftrace language is inspired by awk, C, and predecessor tracers such as DTrace and SystemTap. bpftrace was created by [Alastair Robertson](https://github.com/ajor). - [How to Install and Build](INSTALL.md) - [Manual / Reference Guide](man/adoc/bpftrace.adoc) - [Tutorial](docs/tutorial_one_liners.md) - [Example One-Liners](#example-one-liners) - [Videos](https://bpftrace.org/videos) - [Tools](tools/README.md) - [Release Schedule and Process](docs/release_process.md) - [Contribute](CONTRIBUTING.md) - [Development](CONTRIBUTING.md#development) - [Support](#support) - [Migration guide](docs/migration_guide.md) - [Probe types](#probe-types) - [Plugins](#plugins) - [License](#license) ## Example One-Liners The following one-liners demonstrate different capabilities: ``` # Files opened by thread name bpftrace -e 'tracepoint:syscalls:sys_enter_open { printf("%s %s\n", comm, str(args->filename)); }' # Syscall count by thread name bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' # Read bytes by thread name: bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret/ { @[comm] = sum(args->ret); }' # Read size distribution by thread name: bpftrace -e 'tracepoint:syscalls:sys_exit_read { @[comm] = hist(args->ret); }' # Show per-second syscall rates: bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @ = count(); } interval:s:1 { print(@); clear(@); }' # Trace disk size by PID and thread name bpftrace -e 'tracepoint:block:block_rq_issue { printf("%d %s %d\n", pid, comm, args->bytes); }' # Count page faults by thread name bpftrace -e 'software:faults:1 { @[comm] = count(); }' # Count LLC cache misses by thread name and PID (uses PMCs): bpftrace -e 'hardware:cache-misses:1000000 { @[comm, pid] = count(); }' # Profile user-level stacks at 99 Hertz for PID 189: bpftrace -e 'profile:hz:99 /pid == 189/ { @[ustack] = count(); }' # Files opened in the root cgroup-v2 bpftrace -e 'tracepoint:syscalls:sys_enter_openat /cgroup == cgroupid("/sys/fs/cgroup/unified/mycg")/ { printf("%s\n", str(args->filename)); }' ``` More powerful scripts can easily be constructed. See [Tools](tools/README.md) for examples. ## Support For additional help / discussion, please use our [discussions](https://github.com/bpftrace/bpftrace/discussions) page. We are also holding regular [office hours](https://docs.google.com/document/d/1nt010RfL4s4gydhCPSJ-Z5mnFMFuD4NrcpVmUcyvu2E/edit?usp=sharing) open to the public. ## Probe types
See the [Manual](man/adoc/bpftrace.adoc) for more details. ## Plugins bpftrace has several plugins/definitions, integrating the syntax into your editor. - [Emacs](https://gitlab.com/jgkamat/bpftrace-mode) - [Vim](https://github.com/mmarchini/bpftrace.vim) - [VS Code](https://github.com/bolinfest/bpftrace-vscode) - [Bash Completion](https://github.com/scop/bash-completion) ## License bpftrace is a registered trademark of Alastair Robertson The code is licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. bpftrace-0.23.2/cmake/000077500000000000000000000000001477746507000144655ustar00rootroot00000000000000bpftrace-0.23.2/cmake/CmakeUninstall.cmake.in000066400000000000000000000017471477746507000210170ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later # # Ref: # https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake # if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") endif() file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif() else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif() endforeach() bpftrace-0.23.2/cmake/FindGMock.cmake000066400000000000000000000107451477746507000172770ustar00rootroot00000000000000# Locate the Google C++ Mocking Framework. # (This file is almost an identical copy of the original FindGTest.cmake file, # feel free to use it as it is or modify it for your own needs.) # # # Defines the following variables: # # GMOCK_FOUND - Found the Google Testing framework # GMOCK_INCLUDE_DIRS - Include directories # # Also defines the library variables below as normal # variables. These contain debug/optimized keywords when # a debugging library is found. # # GMOCK_BOTH_LIBRARIES - Both libgmock & libgmock-main # GMOCK_LIBRARIES - libgmock # GMOCK_MAIN_LIBRARIES - libgmock-main # # Accepts the following variables as input: # # GMOCK_ROOT - (as a CMake or environment variable) # The root directory of the gmock install prefix # # GMOCK_MSVC_SEARCH - If compiling with MSVC, this variable can be set to # "MD" or "MT" to enable searching a gmock build tree # (defaults: "MD") # #----------------------- # Example Usage: # # find_package(GMock REQUIRED) # include_directories(${GMOCK_INCLUDE_DIRS}) # # add_executable(foo foo.cc) # target_link_libraries(foo ${GMOCK_BOTH_LIBRARIES}) # #============================================================================= # This file is released under the MIT licence: # # Copyright (c) 2011 Matej Svec # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. #============================================================================= function(_gmock_append_debugs _endvar _library) if(${_library} AND ${_library}_DEBUG) set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) else() set(_output ${${_library}}) endif() set(${_endvar} ${_output} PARENT_SCOPE) endfunction() function(_gmock_find_library _name) find_library(${_name} NAMES ${ARGN} HINTS $ENV{GMOCK_ROOT} ${GMOCK_ROOT} PATH_SUFFIXES ${_gmock_libpath_suffixes} ) mark_as_advanced(${_name}) endfunction() if(NOT DEFINED GMOCK_MSVC_SEARCH) set(GMOCK_MSVC_SEARCH MD) endif() set(_gmock_libpath_suffixes lib) if(MSVC) if(GMOCK_MSVC_SEARCH STREQUAL "MD") list(APPEND _gmock_libpath_suffixes msvc/gmock-md/Debug msvc/gmock-md/Release) elseif(GMOCK_MSVC_SEARCH STREQUAL "MT") list(APPEND _gmock_libpath_suffixes msvc/gmock/Debug msvc/gmock/Release) endif() endif() find_path(GMOCK_INCLUDE_DIR gmock/gmock.h HINTS $ENV{GMOCK_ROOT}/include ${GMOCK_ROOT}/include ) mark_as_advanced(GMOCK_INCLUDE_DIR) if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD") # The provided /MD project files for Google Mock add -md suffixes to the # library names. _gmock_find_library(GMOCK_LIBRARY gmock-md gmock) _gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd) _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main) _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind) else() _gmock_find_library(GMOCK_LIBRARY gmock) _gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd) _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main) _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind) endif() include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY) if(GMOCK_FOUND) set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR}) _gmock_append_debugs(GMOCK_LIBRARIES GMOCK_LIBRARY) _gmock_append_debugs(GMOCK_MAIN_LIBRARIES GMOCK_MAIN_LIBRARY) set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES}) endif() bpftrace-0.23.2/cmake/FindLLDB.cmake000066400000000000000000000026771477746507000170210ustar00rootroot00000000000000# - Try to find LLDB # Once done this will define # # LLDB_FOUND - system has lldb # LLDB_INCLUDE_DIRS - the lldb include directory # LLDB_LIBRARIES - Link these to use lldb # # Copyright (c) 2008 Bernhard Walle # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # if (LLDB_LIBRARIES AND LLDB_INCLUDE_DIRS) set (LLDB_FIND_QUIETLY TRUE) endif (LLDB_LIBRARIES AND LLDB_INCLUDE_DIRS) find_path (LLDB_INCLUDE_DIRS NAMES lldb/API/LLDB.h PATHS ENV CPATH) find_library (LLDB_LIBRARIES NAMES lldb PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) # handle the QUIETLY and REQUIRED arguments and set LLDB_FOUND to TRUE if all listed variables are TRUE include (FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LLDB "Please install the lldb development package" LLDB_LIBRARIES LLDB_INCLUDE_DIRS) if (LLDB_FOUND) add_library(LLDB SHARED IMPORTED) set_target_properties(LLDB PROPERTIES INCLUDE_DIRECTORIES "${LLDB_INCLUDE_DIRS}" IMPORTED_LOCATION "${LLDB_LIBRARIES}") if (LLVM_VERSION_MAJOR VERSION_GREATER 10) set_target_properties(LLDB PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES clang-cpp) else () set_target_properties(LLDB PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES libclang) endif () endif (LLDB_FOUND) mark_as_advanced(LLDB_INCLUDE_DIRS LLDB_LIBRARIES) bpftrace-0.23.2/cmake/FindLibBcc.cmake000066400000000000000000000055301477746507000174110ustar00rootroot00000000000000# - Try to find libbcc # Once done this will define # # LIBBCC_FOUND - system has libbcc # LIBBCC_INCLUDE_DIRS - the libbcc include directory # LIBBCC_LIBRARIES - Link these to use libbcc # LIBBCC_DEFINITIONS - Compiler switches required for using libbcc # LIBBCC_BPF_LIBRARIES - libbcc runtime library # LIBBCC_LOADER_LIBRARY_STATIC - libbcc helper static library (for static compilation) # LIBBCC_BPF_CONTAINS_RUNTIME - whether libbcc_bpf.so has been expanded to contain everything !llvm & !clang # # Note that the shared libbcc binary has libbpf and bcc_loader already compiled in but # the static doesn't. So when creating a static build those have to be included too. if (LIBBCC_LIBRARIES AND LIBBCC_INCLUDE_DIRS) set (LibBcc_FIND_QUIETLY TRUE) endif (LIBBCC_LIBRARIES AND LIBBCC_INCLUDE_DIRS) find_path (LIBBCC_INCLUDE_DIRS NAMES bcc/libbpf.h PATHS ENV CPATH ) find_library (LIBBCC_LIBRARIES NAMES bcc PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH ) find_library (LIBBCC_BPF_LIBRARIES NAMES bcc_bpf PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) find_library (LIBBCC_LOADER_LIBRARY_STATIC NAMES bcc-loader-static PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) set(LIBBCC_ERROR_MESSAGE "Please install the bcc library package, which is required. Depending on your distro, it may be called bpfcclib or bcclib (Ubuntu), bcc-devel (Fedora), or something else. If unavailable, install bcc from source (github.com/iovisor/bcc).") include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBBCC_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBcc ${LIBBCC_ERROR_MESSAGE} LIBBCC_LIBRARIES LIBBCC_INCLUDE_DIRS) # Check bpf_attach_kprobe signature if(${LIBBCC_FOUND}) if(STATIC_LINKING) # libbcc.a is not statically linked with libbpf.a, libelf.a, libz.a, and liblzma.a. # If we do a static bpftrace build, we must link them in. find_package(LibBpf) find_package(LibElf) find_package(LibLzma) if(ANDROID) # libz is part of the Android NDK; link against it dynamically SET(CMAKE_REQUIRED_LIBRARIES ${LIBBCC_BPF_LIBRARIES} ${LIBBPF_LIBRARIES} ${LIBELF_LIBRARIES} ${LIBLZMA_LIBRARIES}) SET(CMAKE_REQUIRED_LINK_OPTIONS "-lz") else() find_package(LibZ) SET(CMAKE_REQUIRED_LIBRARIES ${LIBBCC_BPF_LIBRARIES} ${LIBBPF_LIBRARIES} ${LIBELF_LIBRARIES} ${LIBLZMA_LIBRARIES} ${LIBZ_LIBRARIES}) endif() else() SET(CMAKE_REQUIRED_LIBRARIES ${LIBBCC_LIBRARIES} ${LIBBPF_LIBRARIES}) endif() INCLUDE(CheckCXXSourceCompiles) SET(CMAKE_REQUIRED_INCLUDES ${LIBBCC_INCLUDE_DIRS}) SET(CMAKE_REQUIRED_LIBRARIES ${LIBBCC_BPF_LIBRARIES}) include(CheckSymbolExists) check_symbol_exists(bcc_usdt_foreach ${LIBBCC_INCLUDE_DIRS}/bcc/bcc_usdt.h LIBBCC_BPF_CONTAINS_RUNTIME) SET(CMAKE_REQUIRED_LIBRARIES) SET(CMAKE_REQUIRED_INCLUDES) endif(${LIBBCC_FOUND}) bpftrace-0.23.2/cmake/FindLibBfd.cmake000066400000000000000000000063401477746507000174150ustar00rootroot00000000000000# - Try to find libbfd # Once done this will define # # LIBBFD_FOUND - system has libbfd # LIBBFD_INCLUDE_DIRS - the libbfd include directory # LIBBFD_LIBRARIES - Link these to use libbfd # LIBBFD_DEFINITIONS - Compiler switches required for using libbfd # LIBIBERTY_LIBRARIES - libiberty static library (for static compilation) # LIBZ_LIBRARIES - libz static library (for static compilation) #if (LIBBFD_LIBRARIES AND LIBBFD_INCLUDE_DIRS) # set (LibBpf_FIND_QUIETLY TRUE) #endif (LIBBFD_LIBRARIES AND LIBBFD_INCLUDE_DIRS) find_path (LIBBFD_INCLUDE_DIRS NAMES bfd.h PATHS ENV CPATH) find_library (LIBBFD_LIBRARIES NAMES bfd PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) if (${LIBBFD_LIBRARIES} MATCHES ".*libbfd.a$") set(LIBBFD_STATIC TRUE) else() set(LIBBFD_STATIC FALSE) endif() include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBBFD_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBfd "Please install the libbfd development package" LIBBFD_LIBRARIES LIBBFD_INCLUDE_DIRS) mark_as_advanced(LIBBFD_INCLUDE_DIRS LIBBFD_LIBRARIES) if(${LIBBFD_FOUND}) find_package(LibOpcodes) SET(CMAKE_REQUIRED_LIBRARIES ${LIBBFD_LIBRARIES} ${LIBOPCODES_LIBRARIES}) if(STATIC_LINKING OR LIBBFD_STATIC) # libbfd.so is linked with the required libraries but libbfd.a is not. # So if we do a static bpftrace build or libbfd.so is not available (e.g. on # openSUSE), we must link them manually. # Furthermore, libbfd uses some libc symbols that we must manually link # against if we're not using static libc (which includes such symbols). message(STATUS "Using static libbfd, need to link additional libraries.") find_package(ZLIB) find_library (LIBIBERTY_LIBRARIES NAMES iberty PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) find_library (LIBSFRAME_LIBRARIES NAMES sframe PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) find_library (LIBZSTD_LIBRARIES NAMES zstd PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBIBERTY_LIBRARIES} ${ZLIB_LIBRARIES}) if (LIBSFRAME_LIBRARIES) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBSFRAME_LIBRARIES}) endif(LIBSFRAME_LIBRARIES) if (LIBZSTD_LIBRARIES) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBZSTD_LIBRARIES}) endif(LIBZSTD_LIBRARIES) set(CMAKE_REQUIRED_FLAGS "-Wl,--start-group -Wl,-Bdynamic -Wl,-Bdynamic -lpthread -Wl,-Bdynamic -ldl") endif() INCLUDE(CheckCXXSourceCompiles) CHECK_CXX_SOURCE_COMPILES(" #include // See comment in bfd-disasm.cpp for why this needs to exist #define PACKAGE \"bpftrace-test\" #include #include int main(void) { bfd *abfd = bfd_openr(NULL, NULL); disassembler(bfd_get_arch(abfd), bfd_big_endian(abfd), bfd_get_mach(abfd), abfd); return 0; }" LIBBFD_DISASM_FOUR_ARGS_SIGNATURE) CHECK_CXX_SOURCE_COMPILES(" // See comment in bfd-disasm.cpp for why this needs to exist #define PACKAGE \"bpftrace-test\" #include int main(void) { init_disassemble_info(NULL, NULL, NULL, NULL); return 0; } " LIBBFD_INIT_DISASM_INFO_FOUR_ARGS_SIGNATURE) SET(CMAKE_REQUIRED_LIBRARIES) endif() bpftrace-0.23.2/cmake/FindLibBlazesym.cmake000066400000000000000000000012551477746507000205100ustar00rootroot00000000000000# - Try to find libblazesym # Once done this will define # # LIBBLAZESYM_FOUND - system has libblazesym # LIBBLAZESYM_INCLUDE_DIRS - the libblazesym include directory # LIBBLAZESYM_LIBRARIES - Link these to use libblazesym # find_path (LIBBLAZESYM_INCLUDE_DIRS NAMES blazesym.h PATHS ENV CPATH) find_library (LIBBLAZESYM_LIBRARIES NAMES blazesym_c PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBlazesym "Please install the libblazesym development package" LIBBLAZESYM_LIBRARIES LIBBLAZESYM_INCLUDE_DIRS) mark_as_advanced(LIBBLAZESYM_INCLUDE_DIRS LIBBLAZESYM_LIBRARIES) bpftrace-0.23.2/cmake/FindLibBpf.cmake000066400000000000000000000043231477746507000174300ustar00rootroot00000000000000# - Try to find libbpf # Once done this will define # # LIBBPF_FOUND - system has libbpf # LIBBPF_INCLUDE_DIRS - the libbpf include directory # LIBBPF_LIBRARIES - Link these to use libbpf # LIBBPF_DEFINITIONS - Compiler switches required for using libbpf # LIBBPF_VERSION_MAJOR - Libbpf major version # LIBBPF_VERSION_MINOR - Libbpf minor version #if (LIBBPF_LIBRARIES AND LIBBPF_INCLUDE_DIRS) # set (LibBpf_FIND_QUIETLY TRUE) #endif (LIBBPF_LIBRARIES AND LIBBPF_INCLUDE_DIRS) find_path (LIBBPF_INCLUDE_DIRS NAMES bpf/bpf.h bpf/btf.h bpf/libbpf.h PATHS ENV CPATH) find_library (LIBBPF_LIBRARIES NAMES bpf PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) set(LIBBPF_ERROR_MESSAGE "Please install the libbpf development package") include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBBPF_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBpf ${LIBBPF_ERROR_MESSAGE} LIBBPF_LIBRARIES LIBBPF_INCLUDE_DIRS) # Parse version information out of libbpf_version.h if(EXISTS "${LIBBPF_INCLUDE_DIRS}/bpf/libbpf_version.h") file(READ "${LIBBPF_INCLUDE_DIRS}/bpf/libbpf_version.h" version_header) string(REGEX MATCH "LIBBPF_MAJOR_VERSION[ \t]+([0-9]+)" major_match "${version_header}") string(REGEX MATCH "LIBBPF_MINOR_VERSION[ \t]+([0-9]+)" minor_match "${version_header}") string(REGEX REPLACE "LIBBPF_MAJOR_VERSION[ \t]+" "" LIBBPF_VERSION_MAJOR "${major_match}") string(REGEX REPLACE "LIBBPF_MINOR_VERSION[ \t]+" "" LIBBPF_VERSION_MINOR "${minor_match}") else() set(LIBBPF_VERSION_MAJOR 0) set(LIBBPF_VERSION_MINOR 0) message(SEND_ERROR "Libbpf version is too old and does not have libbpf_version.h, which was introduced in v0.6") endif() mark_as_advanced(LIBBPF_INCLUDE_DIRS LIBBPF_LIBRARIES LIBBPF_VERSION_MAJOR LIBBPF_VERSION_MINOR) 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) { DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts); opts.uprobe_multi.flags = 0; return 0; } " HAVE_LIBBPF_UPROBE_MULTI) SET(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_REQUIRED_LIBRARIES) bpftrace-0.23.2/cmake/FindLibBz2.cmake000066400000000000000000000006421477746507000173560ustar00rootroot00000000000000# - Try to find libbz2 # Once done this will define # # LIBBZ2_FOUND - system has libbz2 # LIBBZ2_LIBRARIES - Link these to use libbz2 find_library(LIBBZ2_LIBRARIES NAMES libbz2.a PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBz2 "Please install the libbz2 package" LIBBZ2_LIBRARIES) mark_as_advanced(LIBBZ2_LIBRARIES) bpftrace-0.23.2/cmake/FindLibCereal.cmake000066400000000000000000000007051477746507000201140ustar00rootroot00000000000000# - Try to find libcereal # Once done this will define # # LIBCEREAL_FOUND - system has libcereal # LIBCEREAL_INCLUDE_DIRS - the libcereal include directory find_path (LIBCEREAL_INCLUDE_DIRS NAMES cereal/cereal.hpp PATHS ENV CPATH) include (FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibCereal "Please install the libcereal development package" LIBCEREAL_INCLUDE_DIRS) mark_as_advanced(LIBCEREAL_INCLUDE_DIRS) bpftrace-0.23.2/cmake/FindLibEbl.cmake000066400000000000000000000007161477746507000174250ustar00rootroot00000000000000# - Try to find libebl static library # Once done this will define # # LIBEBL_FOUND - system has libebl # LIBEBL_LIBRARIES - Link these to use libebl find_library(LIBEBL_LIBRARIES NAMES ebl PATH_SUFFIXES libebl PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEbl "Please install the libebl static library" LIBEBL_LIBRARIES) mark_as_advanced(LIBEBL_LIBRARIES) bpftrace-0.23.2/cmake/FindLibElf.cmake000066400000000000000000000022271477746507000174300ustar00rootroot00000000000000# - Try to find libelf # Once done this will define # # LIBELF_FOUND - system has libelf # LIBELF_INCLUDE_DIRS - the libelf include directory # LIBELF_LIBRARIES - Link these to use libelf # LIBELF_DEFINITIONS - Compiler switches required for using libelf # # Copyright (c) 2008 Bernhard Walle # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # if (LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS) set (LibElf_FIND_QUIETLY TRUE) endif (LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS) find_path (LIBELF_INCLUDE_DIRS NAMES libelf.h PATH_SUFFIXES libelf PATHS ENV CPATH) find_library (LIBELF_LIBRARIES NAMES elf PATH_SUFFIXES libelf PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBELF_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibElf "Please install the libelf development package" LIBELF_LIBRARIES LIBELF_INCLUDE_DIRS) mark_as_advanced(LIBELF_INCLUDE_DIRS LIBELF_LIBRARIES) bpftrace-0.23.2/cmake/FindLibLzma.cmake000066400000000000000000000006551477746507000176300ustar00rootroot00000000000000# - Try to find liblzma # Once done this will define # # LIBLZMA_FOUND - system has liblzma # LIBLZMA_LIBRARIES - Link these to use liblzma find_library(LIBLZMA_LIBRARIES NAMES liblzma.a PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibLzma "Please install the liblzma package" LIBLZMA_LIBRARIES) mark_as_advanced(LIBLZMA_LIBRARIES) bpftrace-0.23.2/cmake/FindLibOpcodes.cmake000066400000000000000000000017221477746507000203150ustar00rootroot00000000000000# - Try to find libbfd # Once done this will define # # LIBOPCODES_FOUND - system has libbfd # LIBOPCODES_INCLUDE_DIRS - the libbfd include directory # LIBOPCODES_LIBRARIES - Link these to use libbfd # LIBOPCODES_DEFINITIONS - Compiler switches required for using libbfd #if (LIBOPCODES_LIBRARIES AND LIBOPCODES_INCLUDE_DIRS) # set (LibBpf_FIND_QUIETLY TRUE) #endif (LIBOPCODES_LIBRARIES AND LIBOPCODES_INCLUDE_DIRS) find_path (LIBOPCODES_INCLUDE_DIRS NAMES dis-asm.h PATHS ENV CPATH) find_library (LIBOPCODES_LIBRARIES NAMES opcodes PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBOPCODES_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibOpcodes "Please install the libopcodes development package" LIBOPCODES_LIBRARIES LIBOPCODES_INCLUDE_DIRS) mark_as_advanced(LIBOPCODES_INCLUDE_DIRS LIBOPCODES_LIBRARIES) bpftrace-0.23.2/cmake/FindLibPcap.cmake000066400000000000000000000016001477746507000175770ustar00rootroot00000000000000# - Try to find libpcap # Once done this will define # # LIBPCAP_INCLUDE_DIRS - where to find pcap/pcap.h # LIBPCAP_LIBRARIES - List of libraries when using pcap # LIBPCAP_FOUND - True if pcap found. find_path(LIBPCAP_INCLUDE_DIRS NAMES pcap/pcap.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ENV CPATH ) find_library(LIBPCAP_LIBRARIES NAMES pcap PATHS /usr/lib /usr/lib64 /usr/local/lib /opt/local/lib /sw/lib ENV LIBRARY_PATH ENV LD_LIBRARY_PATH ) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBPCAP_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibPcap "Please install the libpcap development package" LIBPCAP_LIBRARIES LIBPCAP_INCLUDE_DIRS) mark_as_advanced(LIBPCAP_INCLUDE_DIRS LIBPCAP_LIBRARIES) bpftrace-0.23.2/cmake/FindLibZ.cmake000066400000000000000000000007651477746507000171400ustar00rootroot00000000000000# - Try to find libz # Once done this will define # # LIBZ_FOUND - system has libz # LIBZ_LIBRARIES - Link these to use libz find_library(LIBZ_LIBRARIES NAMES libz.a PATHS ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBZ_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibZ "Please install the libz package" LIBZ_LIBRARIES) mark_as_advanced(LIBZ_LIBRARIES) bpftrace-0.23.2/cmake/Util.cmake000066400000000000000000000010741477746507000164060ustar00rootroot00000000000000# Workaround to remove dynamic libs from static library dependencies function(unlink_transitive_dependency targets dep_to_remove) foreach(tgt ${targets}) if(NOT TARGET ${tgt}) continue() endif() get_target_property(int_link_libs ${tgt} INTERFACE_LINK_LIBRARIES) if(NOT(int_link_libs MATCHES "-NOTFOUND$")) list(REMOVE_ITEM int_link_libs "${dep_to_remove}") set_target_properties(${tgt} PROPERTIES INTERFACE_LINK_LIBRARIES "${int_link_libs}") endif() endforeach(tgt ${targets}) endfunction(unlink_transitive_dependency) bpftrace-0.23.2/cmake/Version.cmake000066400000000000000000000021131477746507000171110ustar00rootroot00000000000000# This file generates the file "version.h", containing an up-to-date version # string on each build. # Inputs: # File names: # VERSION_H_IN - name of template source file # VERSION_H - name of populated file to be generated # Fallback version numbers when not building in a Git repository: # bpftrace_VERSION_MAJOR # bpftrace_VERSION_MINOR # bpftrace_VERSION_PATCH # Try and get a version string from Git tags execute_process( COMMAND git describe --abbrev=4 --dirty --tags WORKING_DIRECTORY ${bpftrace_SOURCE_DIR} OUTPUT_VARIABLE BPFTRACE_VERSION ERROR_VARIABLE GIT_DESCRIBE_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE retcode ) # If the build is not done from a git repo, get the version information from # the version variables in main CMakeLists.txt if(NOT ${retcode} EQUAL 0) set(BPFTRACE_VERSION "v${bpftrace_VERSION_MAJOR}.${bpftrace_VERSION_MINOR}.${bpftrace_VERSION_PATCH}") message(STATUS "Could not obtain version string from Git. Falling back to CMake version string.") endif() configure_file(${VERSION_H_IN} ${VERSION_H} @ONLY) bpftrace-0.23.2/docker/000077500000000000000000000000001477746507000146545ustar00rootroot00000000000000bpftrace-0.23.2/docker/Dockerfile.debian000066400000000000000000000014721477746507000200730ustar00rootroot00000000000000# This Dockerfile is used to both document and test building bpftrace on the # upcoming debian release. We attempt to catch bugs as early as possible which # is why we are using sid/unstable. FROM debian:sid ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y \ asciidoctor \ binutils-dev \ bison \ build-essential \ clang \ cmake \ flex \ libbpf-dev \ libbpfcc-dev \ libcereal-dev \ libelf-dev \ libiberty-dev \ libpcap-dev \ llvm-dev \ liblldb-dev \ libclang-dev \ systemtap-sdt-dev \ zlib1g-dev COPY . /src WORKDIR /src # Use CMAKE_BUILD_TYPE=Release if you don't plan on developing bpftrace RUN cmake -B /build -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Debug RUN make -C /build -j$(nproc) ENTRYPOINT ["/build/src/bpftrace"] bpftrace-0.23.2/docker/Dockerfile.fedora000066400000000000000000000015461477746507000201130ustar00rootroot00000000000000# This Dockerfile is used to both document and test building bpftrace on the # development version of fedora. We attempt to catch bugs as early as possible # which is why we are using rawhide. FROM fedora:rawhide RUN dnf install -y \ asciidoctor \ bison \ binutils-devel \ bcc-devel \ cereal-devel \ clang-devel \ cmake \ elfutils-devel \ elfutils-libelf-devel \ elfutils-libs \ flex \ gcc \ gcc-c++ \ libpcap-devel \ libbpf-devel \ llvm-devel \ make \ systemtap-sdt-devel \ zlib-devel COPY . /src WORKDIR /src # Use CMAKE_BUILD_TYPE=Release if you don't plan on developing bpftrace RUN cmake -B /build -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Debug RUN make -C /build -j$(nproc) ENTRYPOINT ["/build/src/bpftrace"] bpftrace-0.23.2/docker/Dockerfile.static000066400000000000000000000023211477746507000201320ustar00rootroot00000000000000# This Dockerfile is used to test STATIC_LINKING=ON builds in the CI FROM alpine:3.21 RUN apk add --update \ asciidoctor \ argp-standalone \ bash \ bcc-dev \ bcc-static \ binutils-dev \ bison \ bzip2-static \ build-base \ cereal \ clang18-dev \ clang18-extra-tools \ clang18-static \ cmake \ elfutils-dev \ flex-dev \ git \ libbpf-dev \ libelf-static \ libpcap-dev \ libc6-compat \ linux-headers \ llvm18-dev \ llvm18-gtest \ llvm18-static \ musl-obstack-dev \ openssl-dev \ pahole \ procps \ python3 \ wget \ xxd \ xz-static \ zlib-dev \ zlib-static \ zstd-dev \ zstd-static # It looks like llvm18 prefers to dynamically link against zstd. Extremely # unclear why. Work around it by modifying LLVMExports.cmake. RUN sed -i 's/libzstd_shared/libzstd_static/g' /usr/lib/llvm18/lib/cmake/llvm/LLVMExports.cmake # bcc-static needs clang/llvm 18 instead of the latest 19. As a consequence, # CMake reports errors as that it cannot find files in /usr/lib/cmake/clang/ as # it's a symlink for /usr/lib/cmake/clangXX/ which is only created when the # latest clang is installed. To fix this, create the symlink manually. RUN ln -s 'clang18' /usr/lib/cmake/clang bpftrace-0.23.2/docker/Dockerfile.ubuntu000066400000000000000000000014731477746507000201740ustar00rootroot00000000000000# This Dockerfile is used to both document and test building bpftrace on the # development version of ubuntu. We attempt to catch bugs as early as possible # which is why we are using devel. FROM ubuntu:devel ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y \ asciidoctor \ binutils-dev \ bison \ build-essential \ clang \ cmake \ flex \ libbpf-dev \ libbpfcc-dev \ libcereal-dev \ libelf-dev \ libiberty-dev \ libpcap-dev \ llvm-dev \ liblldb-dev \ libclang-dev \ systemtap-sdt-dev \ zlib1g-dev COPY . /src WORKDIR /src # Use CMAKE_BUILD_TYPE=Release if you don't plan on developing bpftrace RUN cmake -B /build -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Debug RUN make -C /build -j$(nproc) ENTRYPOINT ["/build/src/bpftrace"] bpftrace-0.23.2/docs/000077500000000000000000000000001477746507000143355ustar00rootroot00000000000000bpftrace-0.23.2/docs/coding_guidelines.md000066400000000000000000000102131477746507000203270ustar00rootroot00000000000000# Coding guidelines The goal of this document is to establish some common understanding of what language features should and should not be used. This helps reduce mental overhead while reading, developing, and reviewing code. In some cases, it also helps present a more consistent user experience. Discussions about this document should occur in a pull request making changes to the text of this document. This helps keep track of why things are the way they are. As well as providing a structured environment to participate. If something is not called out in this document, defer to [the C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines). In other words, this document supersedes the core guidelines. Note that the checked in code may not adhere to these guidelines yet. This document is intended to be referenced for new code. Ideally we also modify checked-in code to follow these guidelines (if/when there is time). ## Error handling and exceptions If an error is recoverable (meaning downstream callers of the code should be able to selectively handle, ignore, or further propagate), pass the error through the return value of the function. Appropriate language features for this include: * `std:optional` * `int` * `bool` If an error is **not** recoverable, prefer throwing `FatalUserException`. Exceptions **should not** be used for recoverable errors. ### Examples An example of a recoverable error would be failure to write an entry into a map. There are a number of reasons why writing to a map could fail (map is full, insufficient privileges, map does not exist yet, etc.) and we can't know for sure at the point of failure what the reason is. Additionally, this type of error might not be a reason fail the whole program. So we need to propagate the error. An example of an unrecoverable error would be if `debugfs` is not mounted. If debugfs isn't mounted, all bets are off b/c we cannot interact with the kernel debug facilities. And we probably should not be mounting filesystems on behalf of the user. So at the point of failure, we should just throw an exception and let the user know their system is not ready for bpftrace. ## Struct vs. Class Use a struct only for passive objects that carry data; everything else is a class. All fields in a struct should be public. The struct must not have invariants that imply relationships between different fields, since direct user access to those fields may break those invariants. Structs should not have any associated methods (including constructors and destructors). ## Variable naming Variables should be in `snake_case` (all lowercase, with underscores between words). Private members should have a trailing underscore. Public members should not have a trailing underscore. Struct data members should _not_ have a trailing underscore. Examples: ```c++ class Foo { public: int snake_case; // good int secondone; // bad int var_; // bad int camelCase; // bad private: int another_var_; // good int private_var; // bad int priv_; // good }; struct Bar { int var; // good int tailing_; // bad }; ``` ## Log Messages Below are details about when to use each kind of log level: - `DEBUG`: log info regardless of log level; like using stdout (comes with file and line number) - `V1`: log info only if verbose logging is enabled (-v); use this (among others) for printing warnings which may occur on every bpftrace execution (like BTF is not available) - `HINT`: log info that could give tips on how the user can resolve a problem they have encountered. This would normally be used immediately after a LOG(WARNING) or LOG(ERROR). - `WARNING`: log info that might affect bpftrace behavior or output but allows the run to continue; like using stderr - `ERROR`: log info to indicate that the user did something invalid, which will (eventually) cause bpftrace to exit (via `exit(1)`); this should primarily get used in main.cpp after catching `FatalUserException` from deeper parts of the code base - `BUG`: abort and log info to indicate that there is an internal/unexpected issue (not caused by invalid user program code or CLI use) bpftrace-0.23.2/docs/dependency_support.md000066400000000000000000000034261477746507000205760ustar00rootroot00000000000000# Dependency support policy This document outlines our policy for supporting bpftrace's major dependencies. Our support policy for minor dependencies are done on a case by case basis, usually at distro/user request. ## Linux kernel The linux kernel is bpftrace's biggest runtime dependency. Our stance on kernel support is that for **cross cutting** implementation details (ie. something every reasonably sophisticated user is expected to depend on), we'll support up to and including the oldest LTS kernel still in service. For features that have clear boundaries (eg. new builtins or helpers), bpftrace is free to opportunistically depend on newer kernels as long as there is a reasonable runtime fallback strategy. An error message is a reasonable fallback strategy in this case. The source of truth on EOL dates and LTS kernels is https://www.kernel.org/. ## LLVM (dynamically linked) LLVM is bpftrace's biggest build time dependency. The project always supports the latest LLVM release as soon as it's practical (available in CI). We support some number of previous LLVM releases. Given LLVM's twice annual release cadence, we have historically supported somewhere around the last 3 years' worth. We do not provide a hard guarantee, but it's probably safe to the versions from the previous year will be supported. ## LLVM (statically linked) In contrast to dynamically linked LLVM, statically linked LLVM is significantly more difficult to maintain. As a consequence, we only support a single LLVM release in the static build configuration. We do not yet have a policy on when the LLVM version is updated, but we will document any changes in the release notes. Please consult [static.sh][0] for the source of truth. [0]: https://github.com/bpftrace/bpftrace/blob/master/.github/include/static.sh bpftrace-0.23.2/docs/design_principles.md000066400000000000000000000076161477746507000203720ustar00rootroot00000000000000# Design Principles This document exists so that there is a clear understanding of what bpftrace does and does not do. While we are excited to see community contributions, we are not likely to choose a path that goes against these principles. This document is as much for current and future maintainers as it is for new and existing contributors. This is a living document and subject to change. ## bpftrace Mission Statement Provide a quick and easy way for people to write observability-based BPF programs, especially for people unfamiliar with the complexities of eBPF (e.g. the verifier, kernel/userspace interaction, attachment, program loading, memory access, and the various types of BPF maps). ## Language Goals These are in priority order: 1. conciseness / one-liners 1. readability / easy to understand 1. clean abstraction from eBPF 1. ability to quickly iterate 1. composability 1. good performance in both kernel and userspace 1. speed of program initialization/start-up ## Language Non-Goals 1. testability 1. debuggability (no gdb or self-tracing) 1. dynamic typing 1. Classes / Inheritance 1. metaprogramming 1. exception handling 1. BPF security, LSM, XDP, Scheduling 1. BPF concepts that don't pertain to observability or can’t be abstracted cleanly ## Stability We value API stability and we would like bpftrace to work on as many past versions of the Linux kernel as possible ([dependency support](./dependency_support.md)). However, due to the speed of kernel development, especially in the BPF space, we need to ~~keep up~~ PUSH the community forward, which means sometimes we need to break things. We prefer the stability in the sense of “It is heavily used in production, and when something changes, there is a clear migration path†e.g. this [migration guide](./migration_guide.md). When we deprecate a pattern, we study its usage and, when appropriate, add deprecation warnings in an upcoming release before removing/changing it completely. We don’t deprecate anything without a good reason. We recognize that sometimes deprecations warnings cause frustration but we add them because deprecations clean up the road for the improvements and new features that we and many people in the community consider valuable. ## Implementation We try to provide an elegant, intuitive, and surprise-free experience when users are writing/running bpftrace scripts. We are less concerned with the implementation being elegant. The real world is far from perfect, and to a reasonable extent we prefer to write ugly code if it means the user does not have to write it. When we evaluate new code, we are looking for an implementation that is correct, performant, tested, and will not lead to mounds of tech debt. We prefer boring code to clever code. Code is disposable and often changes. So it is important that it doesn’t introduce new internal abstractions unless absolutely necessary. Verbose code that is easy to move around, change, and remove is preferred to elegant code that is prematurely abstracted and hard to change. ## Design Review Many changes, including bug fixes and documentation improvements can be implemented and reviewed via the normal GitHub pull request workflow. Some changes, though, are "substantial" or breaking, and we ask that these be put through the [RFC process](../CONTRIBUTING.md#rfc-process) and produce a consensus among bpftrace's maintainers. This process is intended to provide a consistent and controlled path for changes to bpftrace so that all stakeholders can be confident about the direction of the project. ## Evolution Like many open source projects, bpftrace is evolving. As we learn more about our customers and what they need from a tool that promises fast kernel-based observability/tracing, we will adapt and grow bpftrace to meet those needs. We also want to grow the bpftrace community with transparency, responsiveness, kindness, and collaboration. Let's build something awesome together! bpftrace-0.23.2/docs/developers.md000066400000000000000000000160111477746507000170260ustar00rootroot00000000000000# bpftrace development guide This document features basic guidelines and recommendations on how to do bpftrace development. Please read it carefully before submitting pull requests to simplify reviewing and to speed up the merge process. ## Building The project supports the following recommended build workflows. Please choose the one that works the best for you. ### Nix build Nix is the most convenient way to build and test bpftrace. Nix will manage all of bpftrace's build and runtime dependencies. It also has the advantage of being used by the CI, so you are more likely to shake out errors before submitting your change and seeing the CI fail. The Nix build is documented in [nix.md](./nix.md). ### Distro build The "distro build" is the more traditional way to build bpftrace. It relies on you installing all of bpftrace's build and runtime dependencies on your host and then calling into `cmake`. Please be aware that bpftrace has strict dependencies on new versions of `libbpf` and `bcc`. They are two of bpftrace's most important dependencies and we plan on tracking their upstream quite closely over time. As a result, while the distro build should work well on distros with newer packages, developers on distros that lag more behind (for example Debian) may want to consider using the Nix build. Or manually building and installing `bcc` and `libbpf`. The distro build is documented in [INSTALL.md](../INSTALL.md#generic-build-process). ## [Tests](../tests/README.md) Every contribution should (1) not break the existing tests and (2) introduce new tests if relevant. See existing tests for inspiration on how to write new ones. [Read more on the different kinds and how to run them](../tests/README.md). ## Continuous integration CI executes the above tests in a matrix of different LLVM versions on NixOS. The jobs are defined in `.github/workflows/ci.yml`. ### Running the CI CI is automatically run on all branches and pull requests on the main repo. We recommend to enable the CI (GitHub Actions) on your own fork, too, which will allow you to run the CI against your testing branches. ### Debugging CI failures It may often happen that tests pass on your local setup but fail in one of the CI environments. In such a case, it is useful to reproduce the environment to debug the issue. To reproduce the NixOS jobs (from `.github/workflows/ci.yml`): 1. Acquire the job environment from the GHA UI: ![](../images/ci_job_env.png) 1. Run `.github/include/ci.py` with the relevant environment variables set Example `ci.py` invocations: ``` $ NIX_TARGET=.#bpftrace-llvm10 ./.github/include/ci.py ``` ``` $ NIX_TARGET=.#bpftrace-llvm11 \ CMAKE_BUILD_TYPE=Release \ RUNTIME_TEST_DISABLE="probe.kprobe_offset_fail_size,usdt.usdt probes - file based semaphore activation multi process" \ ./.github/include/ci.py ``` ### Known issues Some tests are known to be flaky and sometimes fail in the CI environment. The list of known such tests: - runtime test `usdt.usdt probes - file based semaphore activation multi process` ([#2410](https://github.com/bpftrace/bpftrace/issues/2402)) What usually helps, is restarting the CI. This is simple on your own fork but requires one of the maintainers for pull requests. ### Virtual machine tests (vmtests) In CI we run a subset of runtime tests under a controlled kernel by taking advantage of nested virtualization on CI runners. For these tests, we use [vmtest](https://github.com/danobi/vmtest) to manage the virtual machine. The instructions in the above "Debugging CI failures" section also work for the vmtest-ed runtime tests. But if you want to manually try something quick and dirty in a CI kernel, you can do something like the following: ```bash $ nix develop (nix:nix-shell-env) $ vmtest -k $(nix build --print-out-paths .#kernel-6_12)/bzImage -- ./build/src/bpftrace -V => bzImage ===> Booting ===> Setting up VM ===> Running command bpftrace v0.21.0-344-g3acb ``` While we'll defer to `vmtest` documentation for full details, one neat fact worth pointing out is that `vmtest` will map the current running userspace into the VM. This means you can run binaries built on your host from inside the guest, eg. your development build of bpftrace. ## Coding guidelines This is not about the formatting of the source code (we have `clang-format` for that). Rather, it's about the semantics of the code and what language features we try to use / avoid. Please see [coding_guidelines.md](./coding_guidelines.md) for a full treatment on the topic. ## Code style We use clang-format with our custom config for formatting code. This was [introduced](https://github.com/bpftrace/bpftrace/pull/639) after a lot of code was already written. Instead of formatting the whole code base at once and breaking `git blame` we're taking an incremental approach, each new/modified bit of code needs to be formatted. The CI checks this too, if the changes don't adhere to our style the job will fail. ### Using clang-format [git clang-format](https://github.com/llvm/llvm-project/blob/main/clang/tools/clang-format/git-clang-format) can be used to easily format commits, e.g. `git clang-format upstream/master` ### Avoid 'fix formatting' commits We want to avoid `fix formatting` commits. Instead every commit should be formatted correctly. ### Comment style Strongly prefer C++-style comments for single line and block comments. C-style comments are still useable for nested comments within a single line, e.g. to leave an annotation on a specific argument or parameter. In the future, there may be considerations for automated documentation based on comments, but this is not currently done. `bpftrace` itself supports both C-style and C++-style comment blocks. There is currently no decision on recommended comment style, and both are used freely. ## Merging pull requests Please squash + rebase all pull requests (with no merge commit). In other words, there should be one commit in master per pull request. This makes generating changelogs both trivial and precise with the least amount of noise. The exception to this is PRs with complicated changes. If this is the case and the commits are well structured, a rebase + merge (no merge commit) is acceptable. The rule of thumb is the commit titles should make sense in a changelog. ## Changelog The changelog is for end users. It should provide them with a quick summary of all changes important to them. Internal changes like refactoring or test changes do not belong to it. ### Maintaining the changelog To avoid having write a changelog when we do a release (which leads to useless changelog or a lot of work) we write them as we go. That means that every PR that has a user impacting change must also include a changelog entry. As we include the PR number in the changelog format this can only be done after the PR has been opened. If it is a single commit PR we include the changelog in that commit, when the PR consists of multiple commits it is OK to add a separate commit for the changelog. ## bpftrace internals For more details on bpftrace internals, see [internals_development.md](internals_development.md). bpftrace-0.23.2/docs/fuzzing.md000066400000000000000000000163431477746507000163620ustar00rootroot00000000000000# Fuzzing bpftrace This document is for bpftrace developers. ## Introduction Fuzzing is a method to find bugs in a program automatically. In fuzzing, a fuzzer generates the program input and give it and observes whether the program crashes or not. The most commonly used fuzzing method is called gray box fuzzing, which uses coverage (which parts the program executes) information to generate input efficiently. Fuzzing can be divided into two types according to the target of fuzzing: one that targets the entire program for fuzzing, such as AFL, and the other that targets a specific function, such as libFuzzer. In the former case, a fuzzer generates and supplies the program's input, so you don't need to modify the program. On the other hand, it is not always efficient for large programs, though, in reality, AFL founds a lot of bugs in many programs. The latter is efficient for a function to be fuzzed because a fuzzer directly targets the function, but we need to write some glue code to connect a fuzzer and the function. ## bpftrace options for fuzzing bpftrace has several options useful for fuzzing. ### `BPFTRACE_MAX_AST_NODES` environment variable When doing fuzzing, it is important to limit the number of AST nodes because otherwise, a fuzzer might keep generating a very long program that causes a stack overflow. `BPFTRACE_MAX_AST_NODES` environment variable controls the maximum number of AST nodes. ## Fuzzing with AFL Here, I briefly describe the way to fuzz bpftrace with AFL. I highly recommend reading the documentation in the AFL's repository for further information. ### Install AFL (or AFLPlusPlus) Please install [AFL](https://github.com/google/AFL) or [AFLPlusPlus](https://github.com/AFLplusplus/AFLplusplus) according to the instructions. I use AFLPlusPlus because it works well in my environment. AFLPlusPlus is a forked version of AFL (there was a time when the AFL wasn't updated for a while. Now, AFL is hosted on Google's github, and the development is continuing). AFL and AFLPlusPlus have almost the same interface. ### Compile for fuzzing To use AFL, we need to compile the program with the AFL's compiler (it's the wrapper of gcc/clang and do some instrumentation for measuring coverage.) Below is an example of a compile. ``` CC=/path/to/AFLplusplus/afl-clang-fast \ CXX=/path/to/AFLplusplus/afl-clang-fast++ \ AFL_USE_ASAN=1 \ cmake .. \ -DBUILD_FUZZ \ -DFUZZ_TARGET=codegen \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTING=0 \ -DBUILD_ASAN=1 ``` then, ``` AFL_USE_ASAN=1 make -j$(nproc) ``` Important points: - `-DBUILD_FUZZ` option is required to build bpftrace for fuzzing. It adds `-DFUZZ` to compile options. - `-DFUZZ_TARGET` is used to let the program stop right after the specified process. The supported value is either "semantic" or "codegen". For example, if `-DFUZZ_TARGET=semantic`, then the program stops after a semantic analysis. - AddressSanitizer might take a lot of memory. If you want to fuzz without it, please remove `AFL_USE_ASAN` and `-DBUILD_ASAN`. ### Let's Fuzzing First, AFL requires some settings for efficient fuzzing. ``` echo core | sudo tee -a /proc/sys/kernel/core_pattern cd /sys/devices/system/cpu echo performance | sudo tee cpu*/cpufreq/scaling_governor ``` Then, start fuzzing! AFL and AddressSanitizer have a lot of settings, so please read each documentation for the details. The sample way to run fuzzer is like below: ``` CPU=0 FUZZER=/path/to/AFLplusplus/afl-fuzz sudo AFL_NO_AFFINITY=1 \ ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:symbolize=0 \ BPFTRACE_MAX_AST_NODES=200 \ taskset -c ${CPU} \ $FUZZER -M 0 -m none -i ./input -o ./output -t 3000 -- \ ./src/bpftrace_fuzz @@ ``` I describe several important things: - `bpftrace_fuzz` is a fuzzer that we built. It's a slightly modified version of bpftrace for fuzzing, and the first argument is the script file name. If the argument is not given, it reads a script from stdin. - `-i` is the input directory, and `-o` is the output directory. In the input directory, you need to put something to start fuzzing. The most simple example is `echo a > input/a`. More sophisticated inputs can be created by extracting the bpftrace program from the source code directory (especially from tests directory. See the "Input corpus creation" section below for the details.) If some inputs that cause a program crash is found, `output/crashes` contains them. - bpftrace has known that it has several memory leaks. Therefore `ASAN_OPTIONS=detect_leaks=0` is needed. Otherwise, fuzzer thinks each memory leak as a crash and report it. Also, `abort_on_error=1: symbolize=0` is required for fuzzing. - `-t 3000` is the timeout value of each execution. Because (especially codegen) sometimes take a long time to process, it is important to have a longer timeout. Otherwise, AFL would stop fuzzing. - `@@` will be replaced by the input file generated by the fuzzer. ## Fuzzing with libFuzzer [LibFuzzer](https://llvm.org/docs/LibFuzzer.html) is a coverage-guided fuzzer developed with llvm/clang, and bpftrace can be fuzzed with it. ### Compile with libFuzzer To use the libFuzzer, use clang and compile it as follows. ``` CC=clang-10 CXX=clang++-10 cmake .. -DBUILD_ASAN=1 -DBUILD_FUZZ=1 -DUSE_LIBFUZZER=1 -DBUILD_TESTING=0 ``` `-DBUILD_FUZZ=1` and `-DUSE_LIBFUZZER=1` is necessary. ### Fuzzing with libFuzzer The compiled binary itself is a fuzzer, and fuzzing can be performed with commands like the following. ``` sudo ASAN_OPTIONS=detect_leaks=0 ./src/bpftrace_fuzz -max_len=1024 input ``` "input" is a corpus of inputs, just as it is used in AFL. Unlike the AFL, the libFuzzer stops when it crashes. ## Input corpus creation Here are some examples of creating input corpus. - Extract scripts from runtime tests (only the ones enclosed in "") ```sh cat ./tests/runtime/scripts/* | grep RUN | grep -- "-e"| \ sed "s/[^']*'\([^']*\)'.*/\1/" | parallel -N1 "echo {} > input/{#}"` ``` - Use bpftrace tools but remove comments and blank lines ```sh find ./tools -name "*.bt" | \ parallel -N1 "sed -e '/^#\!/d' -e '/\/\*.*/d' -e '/^\s\*.*/d' -e '/\/\/.*/d' -e 's/^\s\+//g' -e '/^$/d' {} > input/{#}" ``` ## Found bugs ### AFL - [#1623](https://github.com/bpftrace/bpftrace/pull/1623) - [#1619](https://github.com/bpftrace/bpftrace/pull/1619) - [#1580](https://github.com/bpftrace/bpftrace/pull/1580) - [#1573](https://github.com/bpftrace/bpftrace/pull/1573) - [#1572](https://github.com/bpftrace/bpftrace/pull/1572) - [#1570](https://github.com/bpftrace/bpftrace/pull/1570) - [#1568](https://github.com/bpftrace/bpftrace/pull/1568) - [#1286](https://github.com/bpftrace/bpftrace/pull/1286) - [#1245](https://github.com/bpftrace/bpftrace/pull/1245) - [#1234](https://github.com/bpftrace/bpftrace/pull/1234) - [#1229](https://github.com/bpftrace/bpftrace/pull/1229) - [#1224](https://github.com/bpftrace/bpftrace/pull/1224) - [#1222](https://github.com/bpftrace/bpftrace/pull/1222) - [#1221](https://github.com/bpftrace/bpftrace/pull/1221) - [#1210](https://github.com/bpftrace/bpftrace/pull/1210) - [#1205](https://github.com/bpftrace/bpftrace/pull/1205) ### libFuzzer - [#1653](https://github.com/bpftrace/bpftrace/pull/1653) - [#1650](https://github.com/bpftrace/bpftrace/pull/1650) - [#1622](https://github.com/bpftrace/bpftrace/pull/1622) - [#1621](https://github.com/bpftrace/bpftrace/pull/1621) bpftrace-0.23.2/docs/internals_development.md000066400000000000000000000676571477746507000213050ustar00rootroot00000000000000# bpftrace Internals This document is for bpftrace internals developers. **WARNING:** The information below may be out of date. The codegen tips are still helpful but some of the generated code and verifier errors might have changed. # Codegen This is the most difficult part of bpftrace. It involves writing code like this (from ast/codegen_llvm.cpp): ```C++ AllocaInst *buf = b_.CreateAllocaBPF(call.type, "usym"); b_.CreateMemSet(buf, b_.getInt8(0), call.type.size, 1); Value *pid = b_.CreateLShr(b_.CreateGetPidTgid(), 32); Value *addr_offset = b_.CreateGEP(buf, b_.getInt64(0)); Value *pid_offset = b_.CreateGEP(buf, {b_.getInt64(0), b_.getInt64(8)}); call.vargs->front()->accept(*this); b_.CreateStore(expr_, addr_offset); b_.CreateStore(pid, pid_offset); expr_ = buf; ``` These are llvm [Intermediate Representation](https://en.wikipedia.org/wiki/Intermediate_representation) \(IR\) functions that emit an llvm assembly-like language which can be compiled directly to BPF, thanks to llvm's BPF support. If you use bpftrace -d, you'll see this llvm assembly: ```shell bpftrace -d -e 'kprobe:do_nanosleep { printf("%s is sleeping\n", comm); }' ``` Produces: ```ll ... define i64 @"kprobe:do_nanosleep"(i8*) local_unnamed_addr section "s_kprobe:do_nanosleep" { entry: %comm = alloca [64 x i8], align 1 %printf_args = alloca %printf_t, align 8 %1 = bitcast %printf_t* %printf_args to i8* call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1) store i64 0, %printf_t* %printf_args, align 8 %2 = getelementptr inbounds [64 x i8], [64 x i8]* %comm, i64 0, i64 0 call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2) call void @llvm.memset.p0i8.i64(i8* nonnull %2, i8 0, i64 64, i32 1, i1 false) %get_comm = call i64 inttoptr (i64 16 to i64 (i8*, i64)*)([64 x i8]* nonnull %comm, i64 64) %3 = getelementptr inbounds %printf_t, %printf_t* %printf_args, i64 0, i32 1, i64 0 call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull %3, i8* nonnull %2, i64 64, i32 1, i1 false) %pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1) %get_cpu_id = call i64 inttoptr (i64 8 to i64 ()*)() %perf_event_output = call i64 inttoptr (i64 25 to i64 (i8*, i8*, i64, i8*, i64)*)(i8* %0, i64 %pseudo, i64 %get_cpu_id, %printf_t* nonnull %printf_args, i64 72) call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1) ret i64 0 } ... ``` ## References Reference documentation for the codegen_llvm.cpp IR calls: - [llvm::IRBuilderBase Class Reference](https://llvm.org/doxygen/classllvm_1_1IRBuilderBase.html) - [llvm::IRBuilder Class Template Reference](https://llvm.org/doxygen/classllvm_1_1IRBuilder.html) Reference documentation for the llvm assembly: - [LLVM Language Reference Manual](https://llvm.org/docs/LangRef.html) Reference documentation for eBPF kernel helpers: - [Kernel Helpers](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#helpers) - [`bpf.h`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/bpf.h) Reference for eBPF syscall and data structures (e.g. maps): - [`bpf(2)` man page](http://man7.org/linux/man-pages/man2/bpf.2.html) ## Gotchas If there's one gotcha I would like to mention, it's the use of CreateGEP() (Get Element Pointer). It's needed when dereferencing at an offset in a buffer, and it's tricky to use. Here is [a good video](https://www.youtube.com/watch?v=m8G_S5LwlTo&t=1753s) on how GEP works. ## Verifier BPF programs are submitted to Linux's in-kernel BPF verifier. Read the large comment at the start of the source; it provides a good explanation. Source code: https://github.com/torvalds/linux/blob/master/kernel/bpf/verifier.c Self-tests: https://github.com/torvalds/linux/blob/master/tools/testing/selftests/bpf/test_verifier.c If you see an error, it's often educational to look up the error message inside the source code or tests. The reasoning is easily understood if you work your way backwards from the message. If you find a test which expects your error message: the name of the test may reveal a better explanation for what the error means. You may also find that nearby tests reveal the criteria for success. Reading BPF instructions is difficult, but if you compare "successful tests" against their various "failure tests": you may see patterns and differences. For example, a successful test may perform a bitmask or conditional logic to provide guarantees about what range of inputs is possible. Mostly the verifier is trying to check "are you allowed to read/write this memory?". Usually there's a notion of "if your read begins inside the BPF stack, it needs to end inside the BPF stack as well", or suchlike. For example: if you've stack-allocated a buffer of 64 bytes for writing strings into, and you intend to parameterise "how many bytes might I copy into this buffer": you will need to add some minimum and maximum conditions, to constrain whichever variable is used to determine the length of data copied. BPF load and store instructions may be picky about what locations they're happy to read/write to. For example, probe_read_str() will only permit writes into a PTR_TO_STACK. I've documented some common errors you may encounter when the verifier bounds-checks your program. Most of this was learned during https://github.com/bpftrace/bpftrace/pull/241. ### min value is negative ``` R2 min value is negative, either use unsigned or 'var &= const' ``` Probably you are using a variable to determine "how far should I jump, relative to this pointer". You need to prove that your variable is always positive. You could try casting to unsigned integer (my notes say that this did not result in any improvement, but it feels like it's worth another try): ```c++ // where expr_ is the problematic Value* b_.CreateIntCast( expr_, b_.getInt64Ty(), false) ``` Or you could bitmask it such that no negative number is possible: ```c++ b_.CreateAnd( expr_, 0x7fffffffffffffff) // 64-bit number with all bits except the first set to 1 ``` Or you could try [CreateMaxNum()](https://llvm.org/docs/LangRef.html#llvm-maxnum-intrinsic) (my notes say that this segfaulted, but it feels like it's worth another try): ```c++ b_.CreateMaxNum( b_.getInt64(0), expr_, "ensure_positive"), ``` Or you could try using if/else to provide bounds hints (my notes say that this did not result in any improvement, but it feels like it's worth another try): ```c++ // where expr_ is the problematic Value* // allocate a variable in which to store your final result, after comparisons are completed AllocaInst *mycoolvar = b_.CreateAllocaBPF(b_.getInt64Ty(), "mycoolvar"); Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *positive = BasicBlock::Create(module_->getContext(), "positive", parent); BasicBlock *negative = BasicBlock::Create(module_->getContext(), "negative", parent); BasicBlock *done = BasicBlock::Create(module_->getContext(), "done", parent); b_.CreateCondBr( b_.CreateICmpUGE(expr_, b_.getInt64(0), "if_positive"), positive, negative); // if expr_ is positive, store it into mycoolvar b_.SetInsertPoint(positive); b_.CreateStore(expr_, mycoolvar); b_.CreateBr(done); // if expr_ is negative, store a 0 into mycoolvar (or whatever you want to do) b_.SetInsertPoint(negative); b_.CreateStore(b_.getInt64(0), mycoolvar); b_.CreateBr(done); b_.SetInsertPoint(done); ``` **My favoured approach is to select the result of an unsigned comparison:** ```c++ // largest number we'll allow. choosing arbitrary maximum // since this example just wants to take advantage of the comparison's unsignedness Value *max = b_.getInt64(1024); // integer comparison: unsigned less-than-or-equal-to CmpInst::Predicate P = CmpInst::ICMP_ULE; // check whether expr_ is less-than-or-equal-to maximum Value *Cmp = b_.CreateICmp(P, expr_, max, "str.min.cmp"); // Select will contain expr_ if expr_ is sufficiently low, otherwise it will contain max Value *Select = b_.CreateSelect(Cmp, expr_, max, "str.min.select"); ``` ### unbounded memory access ``` R2 unbounded memory access, use 'var &= const' or 'if (var < const)' ``` You need to prove that you don't jump too far from your pointer. This re-uses techniques from "min value is negative"; you just need to tighten the range even further. How far is too far? You need to [stay below `BPF_MAX_VAR_SIZ`](https://github.com/bpftrace/bpftrace/pull/241#issuecomment-440274294), `1ULL << 29`. So, you could bitmask your variable with `(1ULL << 29) - 1` = `0x1FFFFFFF`: ```c++ b_.CreateAnd( expr_, 0x1fffffff) // (1ULL << 29) - 1 ``` ### invalid stack ``` invalid stack type R1 off=-72 access_size=536870911 ``` This means that it's possible for us to jump so far that we'd overflow our stack. Keep re-using techniques from above, and tighten the range even further. But more likely, you have a fundamental problem: perhaps you're trying to allocate a buffer of arbitrary size (determined at runtime), and do arbitrarily-sized writes into it (determined at runtime). If indeed that's what you're trying to do: you'll have to change your architecture. The BPF stack (512 bytes) can only accommodate tiny allocations and jumps. You need to move towards storing your data in BPF maps. Consider this ongoing discussion on how to rearchitect to store stack data in a map: https://github.com/bpftrace/bpftrace/issues/305 ### expected=PTR_TO_STACK; actual=PTR_TO_MAP_VALUE ``` R1 type=map_value_or_null expected=fp ``` This was encountered when I invoked `probe_read_str(void *dst, int size, const void *unsafe_ptr)` with a `*dst` that pointed to a BPF map value. It refused; `probe_read_str(3)` will only write into stack-allocated memory. The workaround is probably to write data onto the BPF stack _first_, then transfer from BPF stack into BPF map. If you've a lot of data, then this will take a few trips. ### stack limit exceeded ``` Looks like the BPF stack limit of 512 bytes is exceeded. Please move large on stack variables into BPF per-cpu array map. ``` You're trying to stack-allocate a really big variable. Sadly you'll need to rearchitect; see above. ### call to 'memset' is not supported. A call to built-in function 'memset' is not supported. This occurs when you attempt to zero out a large amount of memory, e.g. 1024 bytes. Probably the real problem is that you stack-allocated a really big variable. It just happens that (at large numbers): you'll get the error about memset _before_ you get the error about the allocation. ## Examples. ### 1. Codegen: Sum We can explore and get the hang of llvm assembly by writing some simple C programs and compiling them using clang. Since llvm assembly maps to llvm IR, I've sometimes prototyped my codegen_llvm.cpp IR this way: writing a C program to produce the llvm assembly, and then manually mapping it back to llvm IR. test.c: ```C int test(int arg_a, int arg_b) { int sum; sum = arg_a + arg_b; return sum; } ``` Compiling into llvm assembly: ``` # /usr/bin/clang-6.0 -cc1 test.c -emit-llvm ``` Produces test.ll: ```ll ; ModuleID = 'test.c' source_filename = "test.c" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-linux-gnu" ; Function Attrs: noinline nounwind optnone define i32 @test(i32 %arg_a, i32 %arg_b) #0 { %arg_a.addr = alloca i32, align 4 %arg_b.addr = alloca i32, align 4 %sum = alloca i32, align 4 store i32 %arg_a, i32* %arg_a.addr, align 4 store i32 %arg_b, i32* %arg_b.addr, align 4 %1 = load i32, i32* %arg_a.addr, align 4 %2 = load i32, i32* %arg_b.addr, align 4 %add = add nsw i32 %1, %2 store i32 %add, i32* %sum, align 4 %3 = load i32, i32* %sum, align 4 ret i32 %3 } attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } !llvm.module.flags = !{!0} !llvm.ident = !{!1} !0 = !{i32 1, !"wchar_size", i32 4} !1 = !{!"clang version 6.0.1-svn334776-1~exp1~20180726133222.87 (branches/release_60)"} ``` You can imagine now mapping this back, line by line, to IR. Eg: ```ll %arg_a.addr = alloca i32, align 4 %arg_b.addr = alloca i32, align 4 %sum = alloca i32, align 4 ``` Becomes: ```C++ AllocaInst *arg_a_alloc = b_.CreateAllocaBPF(CreateUInt32(), "arg_a"); AllocaInst *arg_b_alloc = b_.CreateAllocaBPF(CreateUInt32(), "arg_b"); AllocaInst *sum_alloc = b_.CreateAllocaBPF(CreateUInt32(), "sum"); ``` And then: ```ll store i32 %arg_a, i32* %arg_a.addr, align 4 store i32 %arg_b, i32* %arg_b.addr, align 4 ``` Becomes: ```C++ Value *arg_a = test->arg_begin()+0; // haven't explained this bit yet Value *arg_b = test->arg_begin()+1; // " " b_.CreateStore(arg_a, arg_a_alloc); b_.CreateStore(arg_b, arg_b_alloc); ``` And then: ```ll %1 = load i32, i32* %arg_a.addr, align 4 %2 = load i32, i32* %arg_b.addr, align 4 %add = add nsw i32 %1, %2 store i32 %add, i32* %sum, align 4 ``` Becomes: ``` Value *arg_a_load = b_.CreateLoad(arg_a_alloc); Value *arg_b_load = b_.CreateLoad(arg_b_alloc); Value *add = b_.CreateAdd(arg_a_load, arg_b_load); b_.CreateStore(add, sum_alloc); ``` Although I'd probably have written that on one line as: ``` b_.CreateStore(b_.CreateAdd(b_.CreateLoad(arg_a_alloc, arg_b_alloc)), sum_alloc); ``` Finally: ```ll %3 = load i32, i32* %sum, align 4 ret i32 %3 ``` Becomes (I'll just do this on one line as well): ``` b_.CreateRet(b_.CreateLoad(sum_alloc)); ``` That's just my mental conversion. I haven't tested this and it may have a bug. But this should be enough to illustrate the idea. ### 2. Codegen: curtask If you need to add support to a BPF kernel function that bpftrace does not yet call, this is a simple example. It adds a `curtask` builtin that calls BPF_FUNC_get_current_task. See [bcc Kernel Versions](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md) for documentation on these BPF functions. The commit is: https://github.com/bpftrace/bpftrace/commit/895ea46f2c800e2f283339d0c96b3c8209590498 The diff is as simple as such an addition gets, and shows the different files and locations that need to be updated: ```diff diff --git a/README.md b/README.md index 6d72e2a..9cf4d8b 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ Variables: - `arg0`, `arg1`, ... etc. - Arguments to the function being traced - `retval` - Return value from function being traced - `func` - Name of the function currently being traced +- `curtask` - Current task_struct as a u64. Functions: - `hist(int n)` - Produce a log2 histogram of values of `n` diff --git a/src/ast/codegen_llvm.cpp b/src/ast/codegen_llvm.cpp index 27fa477..d3dd1ff 100644 --- a/src/ast/codegen_llvm.cpp +++ b/src/ast/codegen_llvm.cpp @@ -70,6 +70,10 @@ void CodegenLLVM::visit(Builtin &builtin) { expr_ = b_.CreateGetCpuId(); } + else if (builtin.ident == "curtask") + { + expr_ = b_.CreateGetCurrentTask(); + } else if (builtin.ident == "comm") { AllocaInst *buf = b_.CreateAllocaBPF(builtin.type, "comm"); diff --git a/src/ast/irbuilderbpf.cpp b/src/ast/irbuilderbpf.cpp index ccae94c..3ccf1e6 100644 --- a/src/ast/irbuilderbpf.cpp +++ b/src/ast/irbuilderbpf.cpp @@ -307,6 +307,19 @@ CallInst *IRBuilderBPF::CreateGetCpuId() return CreateCall(getcpuid_func, {}, "get_cpu_id"); } +CallInst *IRBuilderBPF::CreateGetCurrentTask() +{ + // u64 bpf_get_current_task(void) + // Return: current task_struct + FunctionType *getcurtask_func_type = FunctionType::get(getInt64Ty(), false); + PointerType *getcurtask_func_ptr_type = PointerType::get(getcurtask_func_type, 0); + Constant *getcurtask_func = ConstantExpr::getCast( + Instruction::IntToPtr, + getInt64(BPF_FUNC_get_current_task), + getcurtask_func_ptr_type); + return CreateCall(getcurtask_func, {}, "get_cur_task"); +} + CallInst *IRBuilderBPF::CreateGetStackId(Value *ctx, bool ustack, size_t limit) { Value *map_ptr = CreateBpfPseudoCall(bpftrace_.stackid_maps_[limit]->mapfd_); diff --git a/src/ast/irbuilderbpf.h b/src/ast/irbuilderbpf.h index 0321e9a..ce2e3b6 100644 --- a/src/ast/irbuilderbpf.h +++ b/src/ast/irbuilderbpf.h @@ -36,6 +36,7 @@ public: CallInst *CreateGetPidTgid(); CallInst *CreateGetUidGid(); CallInst *CreateGetCpuId(); + CallInst *CreateGetCurrentTask(); CallInst *CreateGetStackId(Value *ctx, bool ustack); CallInst *CreateGetJoinMap(Value *ctx); void CreateGetCurrentComm(AllocaInst *buf, size_t size); diff --git a/src/ast/semantic_analyser.cpp b/src/ast/semantic_analyser.cpp index 8eb5744..64c9411 100644 --- a/src/ast/semantic_analyser.cpp +++ b/src/ast/semantic_analyser.cpp @@ -32,6 +32,7 @@ void SemanticAnalyser::visit(Builtin &builtin) builtin.ident == "uid" || builtin.ident == "gid" || builtin.ident == "cpu" || + builtin.ident == "curtask" || builtin.ident == "retval") { builtin.type = CreateUInt64(); } diff --git a/src/lexer.l b/src/lexer.l index c5996b6..3bec616 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -38,7 +38,7 @@ header <(\\.|[_\-\./a-zA-Z0-9])*> {vspace}+ { loc.lines(yyleng); loc.step(); } "//".*$ // Comments -pid|tid|uid|gid|nsecs|cpu|comm|stack|ustack|arg[0-9]|retval|func|name { +pid|tid|uid|gid|nsecs|cpu|comm|stack|ustack|arg[0-9]|retval|func|name|curtask { return Parser::make_BUILTIN(yytext, loc); } {ident} { return Parser::make_IDENT(yytext, loc); } {path} { return Parser::make_PATH(yytext, loc); } diff --git a/tests/codegen.cpp b/tests/codegen.cpp index 38918ca..c00d25f 100644 --- a/tests/codegen.cpp +++ b/tests/codegen.cpp @@ -489,6 +489,42 @@ attributes #1 = { argmemonly nounwind } )EXPECTED"); } +TEST(codegen, builtin_curtask) +{ + test("kprobe:f { @x = curtask }", + +R"EXPECTED(; Function Attrs: nounwind +declare i64 @llvm.bpf.pseudo(i64, i64) #0 + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1 + +define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" { +entry: + %"@x_val" = alloca i64, align 8 + %"@x_key" = alloca i64, align 8 + %get_cur_task = tail call i64 inttoptr (i64 35 to i64 ()*)() + %1 = bitcast i64* %"@x_key" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1) + store i64 0, i64* %"@x_key", align 8 + %2 = bitcast i64* %"@x_val" to i8* + call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2) + store i64 %get_cur_task, i64* %"@x_val", align 8 + %pseudo = tail call i64 @llvm.bpf.pseudo(i64 1, i64 1) + %update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0) + call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1) + call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %2) + ret i64 0 +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1 + +attributes #0 = { nounwind } +attributes #1 = { argmemonly nounwind } +)EXPECTED"); +} + TEST(codegen, builtin_comm) { test("kprobe:f { @x = comm }", diff --git a/tests/parser.cpp b/tests/parser.cpp index cff201b..49b83be 100644 --- a/tests/parser.cpp +++ b/tests/parser.cpp @@ -29,6 +29,7 @@ TEST(Parser, builtin_variables) test("kprobe:f { gid }", "Program\n kprobe:f\n builtin: gid\n"); test("kprobe:f { nsecs }", "Program\n kprobe:f\n builtin: nsecs\n"); test("kprobe:f { cpu }", "Program\n kprobe:f\n builtin: cpu\n"); + test("kprobe:f { curtask }", "Program\n kprobe:f\n builtin: curtask\n"); test("kprobe:f { comm }", "Program\n kprobe:f\n builtin: comm\n"); test("kprobe:f { stack }", "Program\n kprobe:f\n builtin: stack\n"); test("kprobe:f { ustack }", "Program\n kprobe:f\n builtin: ustack\n"); diff --git a/tests/semantic_analyser.cpp b/tests/semantic_analyser.cpp index d6e26b8..d9b24e2 100644 --- a/tests/semantic_analyser.cpp +++ b/tests/semantic_analyser.cpp @@ -67,6 +67,7 @@ TEST(semantic_analyser, builtin_variables) test("kprobe:f { gid }", 0); test("kprobe:f { nsecs }", 0); test("kprobe:f { cpu }", 0); + test("kprobe:f { curtask }", 0); test("kprobe:f { comm }", 0); test("kprobe:f { stack }", 0); test("kprobe:f { ustack }", 0); ``` ### 3. Codegen: arguments & return value See the implementation of `lhist()` for an example of pulling in arguments. Commit: https://github.com/bpftrace/bpftrace/commit/6bdd1198e04392aa468b12357a051816f2cc50e4 You'll also notice that the builtins finish by setting `expr_` to the final result. This is taking the node in the AST and replacing it with the computed expression. Calls don't necessarily do this: for example, `reg()` sets `expr_` since it returns a value, but `printf()` sets `expr_` to `nullptr`, since it does not return a value. ### 4. Codegen: sum(), min(), max(), avg(), stats() These are examples of adding new map functions, and the required components. Since the functions themselves are simple, they are good examples of codegen. They were all added in a single commit: https://github.com/bpftrace/bpftrace/commit/0746ff9c048ed503c606b736ad3a78e141c22890 This also shows the bpftrace components that were added to support these: `BPFtrace::print_map_stats()`, `BPFtrace::max_value()`, `BPFtrace::min_value()`. # Probes Probes are reasonably straightforward. We use libbpf/libbcc, both from [bcc](https://github.com/iovisor/bcc), to create the probes via functions such as `bpf_attach_kprobe()`, `bpf_attach_uprobe()`, and `bpf_attach_tracepoint()`. We also use USDT helpers such as `bcc_usdt_enable_probe()` ## 1. Probes: Interval The addition of the `interval` probe type is a simple example of adding a probe, and the components required: https://github.com/bpftrace/bpftrace/commit/c1e7b05be917ad6fa23a210d047bf9387745bf32 diff: ```diff diff --git a/README.md b/README.md index b73a6d1..4654f65 100644 --- a/README.md +++ b/README.md @@ -157,8 +157,8 @@ Attach script to a statically defined tracepoint in the kernel: Tracepoints are guaranteed to be stable between kernel versions, unlike kprobes. -### timers -Run the script at specified time intervals: +### profile +Run the script on all CPUs at specified time intervals: `profile:hz:99 { ... }` @@ -168,6 +168,13 @@ Run the script at specified time intervals: `profile:us:1500 { ... }` +### interval +Run the script once per interval, for printing interval output: + +`interval:s:1 { ... }` + +`interval:ms:20 { ... }` + ### Multiple attachment points A single probe can be attached to multiple events: diff --git a/src/ast/semantic_analyser.cpp b/src/ast/semantic_analyser.cpp index a08eaf7..2a79553 100644 --- a/src/ast/semantic_analyser.cpp +++ b/src/ast/semantic_analyser.cpp @@ -478,6 +478,15 @@ void SemanticAnalyser::visit(AttachPoint &ap) else if (ap.freq <= 0) err_ << "profile frequency should be a positive integer" << std::endl; } + else if (ap.provider == "interval") { + if (ap.target == "") + err_ << "interval probe must have unit of time" << std::endl; + else if (ap.target != "ms" && + ap.target != "s") + err_ << ap.target << " is not an accepted unit of time" << std::endl; + if (ap.func != "") + err_ << "interval probe must have an integer frequency" << std::endl; + } else if (ap.provider == "BEGIN" || ap.provider == "END") { if (ap.target != "" || ap.func != "") err_ << "BEGIN/END probes should not have a target" << std::endl; diff --git a/src/attached_probe.cpp b/src/attached_probe.cpp index 598ecdc..991111b 100644 --- a/src/attached_probe.cpp +++ b/src/attached_probe.cpp @@ -36,6 +36,7 @@ bpf_prog_type progtype(ProbeType t) case ProbeType::uretprobe: return BPF_PROG_TYPE_KPROBE; break; case ProbeType::tracepoint: return BPF_PROG_TYPE_TRACEPOINT; break; case ProbeType::profile: return BPF_PROG_TYPE_PERF_EVENT; break; + case ProbeType::interval: return BPF_PROG_TYPE_PERF_EVENT; break; default: abort(); } } @@ -61,6 +62,9 @@ AttachedProbe::AttachedProbe(Probe &probe, std::tuple &fun case ProbeType::profile: attach_profile(); break; + case ProbeType::interval: + attach_interval(); + break; default: abort(); } @@ -93,6 +97,7 @@ AttachedProbe::~AttachedProbe() err = bpf_detach_tracepoint(probe_.path.c_str(), eventname().c_str()); break; case ProbeType::profile: + case ProbeType::interval: break; default: abort(); @@ -279,4 +284,35 @@ void AttachedProbe::attach_profile() } } +void AttachedProbe::attach_interval() +{ + int pid = -1; + int group_fd = -1; + int cpu = 0; + + uint64_t period, freq; + if (probe_.path == "s") + { + period = probe_.freq * 1e9; + freq = 0; + } + else if (probe_.path == "ms") + { + period = probe_.freq * 1e6; + freq = 0; + } + else + { + abort(); + } + + int perf_event_fd = bpf_attach_perf_event(progfd_, PERF_TYPE_SOFTWARE, + PERF_COUNT_SW_CPU_CLOCK, period, freq, pid, cpu, group_fd); + + if (perf_event_fd < 0) + throw std::runtime_error("Error attaching probe: " + probe_.name); + + perf_event_fds_.push_back(perf_event_fd); +} + } // namespace bpftrace diff --git a/src/attached_probe.h b/src/attached_probe.h index 86b610c..97036e3 100644 --- a/src/attached_probe.h +++ b/src/attached_probe.h @@ -27,6 +27,7 @@ private: void attach_uprobe(); void attach_tracepoint(); void attach_profile(); + void attach_interval(); Probe &probe_; std::tuple &func_; diff --git a/src/types.cpp b/src/types.cpp index 6813c72..2abaad6 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -57,6 +57,8 @@ ProbeType probetype(const std::string &type) return ProbeType::tracepoint; else if (type == "profile") return ProbeType::profile; + else if (type == "interval") + return ProbeType::interval; abort(); } diff --git a/src/types.h b/src/types.h index 4c4524a..6c94eac 100644 --- a/src/types.h +++ b/src/types.h @@ -52,6 +52,7 @@ enum class ProbeType uretprobe, tracepoint, profile, + interval, }; std::string typestr(Type t); diff --git a/tests/bpftrace.cpp b/tests/bpftrace.cpp index 3c3b036..50b6538 100644 --- a/tests/bpftrace.cpp +++ b/tests/bpftrace.cpp @@ -59,6 +59,14 @@ void check_profile(Probe &p, const std::string &unit, int freq, const std::strin EXPECT_EQ("profile:" + unit + ":" + std::to_string(freq), p.name); } +void check_interval(Probe &p, const std::string &unit, int freq, const std::string &prog_name) +{ + EXPECT_EQ(ProbeType::interval, p.type); + EXPECT_EQ(freq, p.freq); + EXPECT_EQ(prog_name, p.prog_name); + EXPECT_EQ("interval:" + unit + ":" + std::to_string(freq), p.name); +} + void check_special_probe(Probe &p, const std::string &attach_point, const std::string &prog_name) { EXPECT_EQ(ProbeType::uprobe, p.type); @@ -309,6 +317,22 @@ TEST(bpftrace, add_probes_profile) check_profile(bpftrace.get_probes().at(0), "ms", 997, probe_prog_name); } +TEST(bpftrace, add_probes_interval) +{ + ast::AttachPoint a("interval", "s", 1); + ast::AttachPointList attach_points = { &a }; + ast::Probe probe(&attach_points, nullptr, nullptr); + + StrictMock bpftrace; + + EXPECT_EQ(0, bpftrace.add_probe(probe)); + EXPECT_EQ(1, bpftrace.get_probes().size()); + EXPECT_EQ(0, bpftrace.get_special_probes().size()); + + std::string probe_prog_name = "interval:s:1"; + check_interval(bpftrace.get_probes().at(0), "s", 1, probe_prog_name); +} + std::pair, std::vector> key_value_pair_int(std::vector key, int val) { std::pair, std::vector> pair; diff --git a/tests/parser.cpp b/tests/parser.cpp index 786f3d0..d2db79b 100644 --- a/tests/parser.cpp +++ b/tests/parser.cpp @@ -260,6 +260,14 @@ TEST(Parser, profile_probe) " int: 1\n"); } +TEST(Parser, interval_probe) +{ + test("interval:s:1 { 1 }", + "Program\n" + " interval:s:1\n" + " int: 1\n"); +} + TEST(Parser, multiple_attach_points_kprobe) { test("BEGIN,kprobe:sys_open,uprobe:/bin/sh:foo,tracepoint:syscalls:sys_enter_* { 1 }", ``` bpftrace-0.23.2/docs/migration_guide.md000066400000000000000000000103031477746507000200220ustar00rootroot00000000000000# Breaking changes (migration guide) This document lists changes introduced in bpftrace which break backwards compatibility in some way. Each entry should contain: - a link to the PR introducing the change - a brief description of the change - an example of an error message - a simple guide to fix existing scripts ## Versions 0.21.x (or earlier) to 0.22.x (or later) ### Added block scoping for scratch variables https://github.com/bpftrace/bpftrace/pull/3367 Previously, scratch variables were "probe" scoped meaning the following was valid syntax: ``` BEGIN { if (0) { $x = "hello"; } print(($x)); } // prints an empty line ``` However, the value of `$x` at the print statement was considered undefined behavior. Issue: https://github.com/bpftrace/bpftrace/issues/3017 Now variables are "block" scoped and the the above will throw an error at the print statement: "ERROR: Undefined or undeclared variable: $x". If you see this error you can do multiple things to resolve it. **Option 1: Initialize variable before use** ``` BEGIN { $x = ""; if (0) { $x = "hello"; } print(($x)); } ``` **Option 2: Declare variable before use** ``` BEGIN { let $x; // let $x = ""; is also valid if (0) { $x = "hello"; } print(($x)); } ``` Declaring is useful for variables that hold internal bpftrace types e.g. the type returned by the `macaddr` function. This is also not valid even though `$x` is set in both branches (`$x` still needs to exist in the outer scope): ``` BEGIN { if (0) { $x = "hello"; } else { $x = "bye"; } print(($x)); } ``` Additionally, scratch variable shadowing is not allowed e.g. this is not valid: ``` BEGIN { let $x; if (0) { let $x = "hello"; // shadows $x in the parent scope } } ``` ### multi-key `delete` removed https://github.com/bpftrace/bpftrace/pull/3506 This map `delete` syntax is no longer valid: ``` delete(@b[1], @b[2], @b[3]); ``` And will yield this error: ``` # bpftrace -e 'BEGIN { @b[1] = 1; delete(@b[1], @b[2], @b[3]); }' stdin:1:20-47: ERROR: delete() takes up to 2 arguments (3 provided) BEGIN { @b[1] = 1; delete(@b[1], @b[2], @b[3]); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` You might also see this error: ``` # bpftrace -e 'BEGIN { @b[1] = 1; delete(@b[1], @b[2]); }' stdin:1:20-32: ERROR: delete() expects a map with no keys for the first argument BEGIN { @b[1] = 1; delete(@b[1], @b[2]); } ~~~~~~~~~~~~ ``` `delete` now expects only two arguments: a map and a key. For example, the above delete statement should be rewritten as this: ``` delete(@b, 1); delete(@b, 2); delete(@b, 3); ``` And for maps with multiple values as keys, which are represented as a tuple, the delete call looks like this: ``` @c[1, "hello"] = 1; delete(@c, (1, "hello")); ``` ### `pid` and `tid` builtins return `uint32` https://github.com/bpftrace/bpftrace/pull/3441 Previously, `pid` and `tid` builtins returned `uint64` so it is now possible to get an error when storing the builtin in a variable and overriding it with `uint64` later: ``` # bpftrace -e 'BEGIN { $x = pid; $x = cgroup; }' # cgroup is uint64 stdin:1:19-30: ERROR: Integer size mismatch. Assignment type 'uint64' is larger than the variable type 'uint32'. BEGIN { $x = pid; $x = cgroup; } ~~~~~~~~~~~ ``` To mitigate such an error, just typecast `pid` or `tid` to `uint64`: ``` # bpftrace -e 'BEGIN { $x = (uint64)pid; $x = cgroup; }' Attaching 1 probe... ``` ### default `SIGUSR1` handler removed https://github.com/bpftrace/bpftrace/pull/3522 Previously, if the bpftrace process received a `SIGUSR1` signal, it would print all maps to stdout: ``` # bpftrace -e 'BEGIN { @b[1] = 2; }' & kill -s USR1 $(pidof bpftrace) ... @b[1]: 2 ``` This behavior is no longer supported and has been replaced with the ability to define custom handling probes: ``` # bpftrace -e 'self:signal:SIGUSR1 { print("hello"); }' & kill -s USR1 $(pidof bpftrace) ... hello ``` To retain the previous functionality of printing maps, you need to manually include the print statements in your signal handler probe: ``` # bpftrace -e 'BEGIN { @b[1] = 2; } self:signal:SIGUSR1 { print(@b); }' & kill -s USR1 $(pidof bpftrace) ``` bpftrace-0.23.2/docs/nix.md000066400000000000000000000051331477746507000154570ustar00rootroot00000000000000# Building and testing with Nix Nix flakes are, in theory, guaranteed to be 100% reproducible on (nearly) any system. It does this by fully managing every dependency. This also means that you as a developer do not need to install _any_ build / runtime packages to build bpftrace with Nix. Rather than explain how Nix works (which is difficult to impossible in this kind of document), the rest of this guide will be a series of examples. Learning Nix flakes and the Nix language will be an exercise left to the reader. ## Examples These examples all assume you've already installed the `nix` CLI tool. If not, see: https://nixos.org/download.html. Also note again that we require _no dependencies_ to be installed other than `nix` itself. ### Enable flake support Nix flakes are technically an experimental feature but it's widely used and understood that the interface is unlikely to change. To enable flakes, run: ``` $ mkdir -p ~/.config/nix $ echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf ``` ### Build bpftrace ``` $ nix build $ sudo ./result/bin/bpftrace -e 'BEGIN { print("hello world!") }' Attaching 1 probe... hello world! ^C ``` ### Build bpftrace with a different LLVM version ``` $ nix build .#bpftrace-llvm13 $ sudo ./result/bin/bpftrace --info 2>&1 | grep LLVM LLVM: 13.0.1 ``` ### Build bpftrace as a statically linked binary ``` $ nix build .#appimage $ ldd ./result not a dynamic executable $ sudo ./result -e 'BEGIN { print("static!"); exit() }' Attaching 1 probe... static! ``` ### Don't use Nix to build, but rather only manage dependencies ``` $ nix develop [dxu@kashmir bpftrace]$ cmake -B build-nix -GNinja [...] [dxu@kashmir bpftrace]$ ninja -C build-nix [...] [dxu@kashmir bpftrace]$ exit $ sudo ./build-nix/src/bpftrace --version bpftrace v0.17.0-75-g68ea-dirty ``` `nix develop` opens a developer shell. We've configured the bpftrace flake to be nearly the exact same as the default build environment except with a few more tools available. ### Build bpftrace with a different LLVM in developer shell ``` $ nix develop .#bpftrace-llvm18 dxu@kashmir bpftrace]$ cmake -B build-nix -GNinja [...] -- Found LLVM 18.1.7: /nix/store/50fcd75v40wca7vdk9bypgcvv6xhkfhx-llvm-18.1.7-dev/lib/cmake/llvm [...] ``` ### Run test suite inside developer shell ``` $ nix develop [dxu@kashmir bpftrace]$ cd build-nix; sudo ctest -V [...] ``` ## Internal examples This section has a few examples on how to interact with the Nix configuration. ### Format `*.nix` files ``` $ nix fmt 0 / 1 have been reformatted ``` ### Check `*.nix` files for errors ``` $ nix flake check ``` bpftrace-0.23.2/docs/reference_guide.md000066400000000000000000000002241477746507000177700ustar00rootroot00000000000000# bpftrace Reference Guide All details about the bpftrace language, usage, and examples have been moved to the [manual](../man/adoc/bpftrace.adoc).bpftrace-0.23.2/docs/release_process.md000066400000000000000000000111271477746507000200370ustar00rootroot00000000000000# Upcoming release schedule The schedule for the upcoming v0.23 release is: - February 25, 2025: Create release branch `release/0.23.x`. - **March 25, 2025: Release v0.23.0.** # Release procedure This document describes the bpftrace release process. ## Semantic versioning We choose to follow semantic versioning. Note that this doesn't matter much for major version < 1 but will matter a lot for >= 1.0.0 releases. See https://semver.org/. ## Release cadence bpftrace is released twice a year. Since our biggest dependency, which also tends to break things, is LLVM, we align with the [LLVM release schedule](https://llvm.org/docs/HowToReleaseLLVM.html). In particular, a minor bpftrace release should happen **two weeks after a major LLVM release**. In addition, four weeks before the bpftrace release, we create a stabilized release branch, which will only receive bug fixes affecting the release itself. The branch will also serve as a target for future (post-release) bug fixes that should get into that minor release (by creating a new "patch" release). Overview of the release cadence is as follows: | Task | Approximate date | Details | | ---------------------- | ----------------------------------- | -------------------------------------------------------------------- | | release branch created | **2 weeks before the LLVM release** | [Creating a release branch](#creating-a-release-branch) | | LLVM release | usually second Tuesday of Mar/Sep | [LLVM release schedule](https://llvm.org/docs/HowToReleaseLLVM.html) | | bpftrace release | **2 weeks after the LLVM release** | [Tagging a release](#tagging-a-release) | ## Creating a release branch A release branch should be created four weeks before the planned bpftrace release. From that moment, only relevant bug fixes should be backported to the branch. The purpose of this release branch is to give sufficient time to test features in the upcoming bpftrace release without blocking the development on the master branch. When creating a branch, the following steps should be performed. Any changes to the code should be done in the master branch first and then backported to the release branch. In the rare case when the master-first approach is not possible (e.g. a feature present exclusively on master blocks the LLVM update), the changes can be done in the release branch first and forward-ported to master afterwards. 1. Create a new branch according to the [Branching model](#branching-model). 1. Update Nixpkgs to the latest version to get the latest (pre-release) LLVM by running ``` nix flake update ``` and committing the `flake.lock` changes to the repo. At this time, the `-rc2` or `-rc3` version of LLVM should be available. 1. Bump the supported LLVM version in [CMakeLists.txt](../CMakeLists.txt) and [flake.nix](../flake.nix), resolve any potential issues, and add a CI job to [.github/workflows/ci.yml](../.github/workflows/ci.yml) for the new version. 1. Once the final LLVM is released and present in Nixpkgs (usually 2-5 days after the LLVM release), repeat step 2 to get the released LLVM in the CI environment. ### Branching model There should be one release branch per "major release" (we are currently pre-1.0, "major" refers to semver minor version). The name should follow the format `release/..x`. Example branch names: * release/0.21.x * release/1.0.x * release/1.1.x ## Tagging a release You must do the following steps to formally release a version. In the release branch: 1. Mark the release in [CHANGELOG.md](../CHANGELOG.md) by replacing the `## Unreleased` header with `## [VERSION] date`. 1. Update `bpftrace_VERSION_MAJOR`, `bpftrace_VERSION_MINOR`, and `bpftrace_VERSION_PATCH` in [CMakeLists.txt](../CMakeLists.txt) to the target version. 1. Tag a release. We do this in the github UI by clicking "releases" (on same line as "commits"), then "Draft a new release". The tag version and release title should be the same and in `vX.Y.Z` format. The tag description should be the same as what you added to CHANGELOG.md. 1. Check that automation picks up the new release and uploads release assets to the release. 1. If automation fails, please fix the automation for next time and also manually build+upload artifacts by running `scripts/create-assets.sh` from bpftrace root dir and attach the generated archives to the release. Once the release is out: 1. Forward-port the CHANGELOG.md changes from the release branch to master. bpftrace-0.23.2/docs/tutorial_one_liners.md000066400000000000000000000421031477746507000207370ustar00rootroot00000000000000# The bpftrace One-Liner Tutorial This teaches you bpftrace for Linux in 12 easy lessons, where each lesson is a one-liner you can try running. This series of one-liners introduces concepts which are summarized as bullet points. For a full reference to bpftrace, see the [Man page](../man/adoc/bpftrace.adoc) Contributed by Brendan Gregg, Netflix (2018), based on his FreeBSD [DTrace Tutorial](https://wiki.freebsd.org/DTrace/Tutorial). Note: bpftrace 0.19 changed the way probe arguments are accessed (using `args.xxx` instead of `args->xxx`). If you are using an older version of bpftrace, you will need to use `args->xxx` in the below examples. # Lesson 1. Listing Probes ``` bpftrace -l 'tracepoint:syscalls:sys_enter_*' ``` "bpftrace -l" lists all probes, and a search term can be added. - A probe is an instrumentation point for capturing event data. - The supplied search term supports wildcards/globs (`*` and `?`) - "bpftrace -l" can also be piped to grep(1) for full regular expression searching. # Lesson 2. Hello World ``` # bpftrace -e 'BEGIN { printf("hello world\n"); }' Attaching 1 probe... hello world ^C ``` This prints a welcome message. Run it, then hit Ctrl-C to end. - The word `BEGIN` is a special probe that fires at the start of the program (like awk's BEGIN). You can use it to set variables and print headers. - An action can be associated with probes, in { }. This example calls printf() when the probe fires. # Lesson 3. File Opens ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args.filename)); }' Attaching 1 probe... snmp-pass /proc/cpuinfo snmp-pass /proc/stat snmpd /proc/net/dev snmpd /proc/net/if_inet6 ^C ``` This traces file opens as they happen, and we're printing the process name and pathname. - It begins with the probe `tracepoint:syscalls:sys_enter_openat`: this is the tracepoint probe type (kernel static tracing), and is instrumenting when the `openat()` syscall begins (is entered). Tracepoints are preferred over kprobes (kernel dynamic tracing, introduced in lesson 6), since tracepoints have stable API. Note: In modern Linux systems (glibc >= 2.26) the `open` wrapper always calls the `openat` syscall. - `comm` is a builtin variable that has the current process's name. Other similar builtins include pid and tid. - `args` is a struct containing all the tracepoint arguments. This struct is automatically generated by bpftrace based tracepoint information. The members of this struct can be found with: `bpftrace -vl tracepoint:syscalls:sys_enter_openat`. - `args.filename` accesses the `args` struct and gets the value of the `filename` member. - `str()` turns a pointer into the string it points to. # Lesson 4. Syscall Counts By Process ``` bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' Attaching 1 probe... ^C @[bpftrace]: 6 @[systemd]: 24 @[snmp-pass]: 96 @[sshd]: 125 ``` This summarizes syscalls by process name, printing a report on Ctrl-C. - @: This denotes a special variable type called a map, which can store and summarize data in different ways. You can add an optional variable name after the @, eg "@num", either to improve readability, or to differentiate between more than one map. - []: The optional brackets allow a key to be set for the map, much like an associative array. - count(): This is a map function – the way it is populated. count() counts the number of times it is called. Since this is saved by comm, the result is a frequency count of system calls by process name. Maps are automatically printed when bpftrace ends (eg, via Ctrl-C). # Lesson 5. Distribution of read() Bytes ``` # bpftrace -e 'tracepoint:syscalls:sys_exit_read /pid == 18644/ { @bytes = hist(args.ret); }' Attaching 1 probe... ^C @bytes: [0, 1] 12 |@@@@@@@@@@@@@@@@@@@@ | [2, 4) 18 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 30 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64, 128) 19 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [128, 256) 1 |@ ``` This summarizes the return value of the sys_read() kernel function for PID 18644, printing it as a histogram. - /.../: This is a filter (aka predicate), which acts as a filter for the action. The action is only executed if the filtered expression is true, in this case, only for the process ID 18644. Boolean operators are supported ("&&", "||"). - ret: This is the return value of the function. For sys_read(), this is either -1 (error) or the number of bytes successfully read. - @: This is a map similar to the previous lesson, but without any keys ([]) this time, and the name "bytes" which decorates the output. - hist(): This is a map function which summarizes the argument as a power-of-2 histogram. The output shows rows that begin with interval notation, where, for example `[128, 256)` means that the value is: 128 <= value < 256. The next number is the count of occurrences, and then an ASCII histogram is printed to visualize that count. The histogram can be used to study multi-modal distributions. - Other map functions include lhist() (linear hist), count(), sum(), avg(), min(), and max(). # Lesson 6. Kernel Dynamic Tracing of read() Bytes ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = lhist(retval, 0, 2000, 200); }' Attaching 1 probe... ^C @bytes: (...,0] 0 | | [0, 200) 66 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [200, 400) 2 |@ | [400, 600) 3 |@@ | [600, 800) 0 | | [800, 1000) 5 |@@@ | [1000, 1200) 0 | | [1200, 1400) 0 | | [1400, 1600) 0 | | [1600, 1800) 0 | | [1800, 2000) 0 | | [2000,...) 39 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ``` Summarize read() bytes as a linear histogram, and traced using kernel dynamic tracing. - It begins with the probe `kretprobe:vfs_read`: this is the kretprobe probe type (kernel dynamic tracing of function returns) instrumenting the `vfs_read()` kernel function. There is also the kprobe probe type (shown in the next lesson), to instrument when functions begin execution (are entered). These are powerful probe types, letting you trace tens of thousands of different kernel functions. However, these are "unstable" probe types: since they can trace any kernel function, there is no guarantee that your kprobe/kretprobe will work between kernel versions, as the function names, arguments, return values, and roles may change. Also, since it is tracing the raw kernel, you'll need to browse the kernel source to understand what these probes, arguments, and return values, mean. - lhist(): this is a linear histogram, where the arguments are: value, min, max, step. The first argument (`retval`) of vfs_read() is the return value: the number of bytes read. # Lesson 7. Timing read()s ``` # bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ { @ns[comm] = hist(nsecs - @start[tid]); delete(@start, tid); }' Attaching 2 probes... [...] @ns[snmp-pass]: [0, 1] 0 | | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 27 |@@@@@@@@@ | [512, 1k) 125 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [1k, 2k) 22 |@@@@@@@ | [2k, 4k) 1 | | [4k, 8k) 10 |@@@ | [8k, 16k) 1 | | [16k, 32k) 3 |@ | [32k, 64k) 144 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64k, 128k) 7 |@@ | [128k, 256k) 28 |@@@@@@@@@@ | [256k, 512k) 2 | | [512k, 1M) 3 |@ | [1M, 2M) 1 | | ``` Summarize the time spent in read(), in nanoseconds, as a histogram, by process name. - @start[tid]: This uses the thread ID as a key. There may be many reads in-flight, and we want to store a start timestamp to each. How? We could construct a unique identifier for each read, and use that as the key. But because kernel threads can only be executing one syscall at a time, we can use the thread ID as the unique identifier, as each thread cannot be executing more than one. - nsecs: Nanoseconds since boot. This is a high resolution timestamp counter than can be used to time events. - /@start[tid]/: This filter checks that the start time was seen and recorded. Without this filter, this program may be launched during a read and only catch the end, resulting in a time calculation of now - zero, instead of now - start. - delete(@start, tid): this frees the variable. # Lesson 8. Count Process-Level Events ``` # bpftrace -e 'tracepoint:sched:sched* { @[probe] = count(); } interval:s:5 { exit(); }' Attaching 25 probes... @[tracepoint:sched:sched_wakeup_new]: 1 @[tracepoint:sched:sched_process_fork]: 1 @[tracepoint:sched:sched_process_exec]: 1 @[tracepoint:sched:sched_process_exit]: 1 @[tracepoint:sched:sched_process_free]: 2 @[tracepoint:sched:sched_process_wait]: 7 @[tracepoint:sched:sched_wake_idle_without_ipi]: 53 @[tracepoint:sched:sched_stat_runtime]: 212 @[tracepoint:sched:sched_wakeup]: 253 @[tracepoint:sched:sched_waking]: 253 @[tracepoint:sched:sched_switch]: 510 ``` Count process-level events for five seconds, printing a summary. - sched: The `sched` probe category has high-level scheduler and process events, such as fork, exec, and context switch. - probe: The full name of the probe. - interval:s:5: This is a probe that fires once every 5 seconds, on one CPU only. It is used for creating script-level intervals or timeouts. - exit(): This exits bpftrace. # Lesson 9. Profile On-CPU Kernel Stacks ``` # bpftrace -e 'profile:hz:99 { @[kstack] = count(); }' Attaching 1 probe... ^C [...] @[ filemap_map_pages+181 __handle_mm_fault+2905 handle_mm_fault+250 __do_page_fault+599 async_page_fault+69 ]: 12 [...] @[ cpuidle_enter_state+164 do_idle+390 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 22122 ``` Profile kernel stacks at 99 Hertz, printing a frequency count. - profile:hz:99: This fires on all CPUs at 99 Hertz. Why 99 and not 100 or 1000? We want frequent enough to catch both the big and small picture of execution, but not too frequent as to perturb performance. 100 Hertz is enough. But we don't want 100 exactly, as sampling may occur in lockstep with other timed activities, hence 99. - kstack: Returns the kernel stack trace. This is used as a key for the map, so that it can be frequency counted. The output of this is ideal to be visualized as a flame graph. There is also `ustack` for the user-level stack trace. # Lesson 10. Scheduler Tracing ``` # bpftrace -e 'tracepoint:sched:sched_switch { @[kstack] = count(); }' ^C [...] @[ __schedule+697 __schedule+697 schedule+50 schedule_timeout+365 xfsaild+274 kthread+248 ret_from_fork+53 ]: 73 @[ __schedule+697 __schedule+697 schedule_idle+40 do_idle+356 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 305 ``` This counts stack traces that led to context switching (off-CPU) events. The above output has been truncated to show the last two only. - sched: The sched category has tracepoints for different kernel CPU scheduler events: sched_switch, sched_wakeup, sched_migrate_task, etc. - sched_switch: This probe fires when a thread leaves CPU. This will be a blocking event: eg, waiting on I/O, a timer, paging/swapping, or a lock. - kstack: A kernel stack trace. - sched_switch fires in thread context, so that the stack refers to the thread who is leaving. As you use other probe types, pay attention to context, as comm, pid, kstack, etc, may not refer to the target of the probe. # Lesson 11. Block I/O Tracing ``` # bpftrace -e 'tracepoint:block:block_rq_issue { @ = hist(args.bytes); }' Attaching 1 probe... ^C @: [0, 1] 1 |@@ | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 0 | | [4K, 8K) 24 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 2 |@@@@ | [16K, 32K) 6 |@@@@@@@@@@@@@ | [32K, 64K) 5 |@@@@@@@@@@ | [64K, 128K) 0 | | [128K, 256K) 1 |@@ | ``` Block I/O requests by size in bytes, as a histogram. - tracepoint:block: The block category of tracepoints traces various block I/O (storage) events. - block_rq_issue: This fires when an I/O is issued to the device. - args.bytes: This is a member from the tracepoint block_rq_issue arguments which shows the size in bytes. The context of this probe is important: this fires when the I/O is issued to the device. This often happens in process context, where builtins like comm will show you the process name, but it can also happen from kernel context (eg, readahead) when the pid and comm will not show the application you expect. # Lesson 12. Kernel Struct Tracing ``` # cat path.bt #ifndef BPFTRACE_HAVE_BTF #include #include #endif kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } # bpftrace path.bt Attaching 1 probe... open path: dev open path: if_inet6 open path: retrans_time_ms [...] ``` This uses kernel dynamic tracing of the vfs_open() function, which has a (struct path *) as the first argument. - kprobe: As mentioned earlier, this is the kernel dynamic tracing probe type, which traces the entry of kernel functions (use kretprobe to trace their returns). - `arg0` is a builtin variable containing the first probe argument, the meaning of which is defined by the probe type. For `kprobe`, it is the first argument to the function. Other arguments can be accessed as arg1, ..., argN. - `((struct path *)arg0)->dentry->d_name.name`: this casts `arg0` as `struct path *`, then dereferences dentry, etc. - #include: these are necessary to include struct definitions for path and dentry on systems where the kernel was built without BTF (BPF Type Format) data. The kernel struct support is the same as bcc, making use of kernel headers. This means that many structs are available, but not everything, and sometimes it might be necessary to manually include a struct. For an example of this, see the [dcsnoop tool](../tools/dcsnoop.bt), which includes a portion of struct nameidata manually as it wasn't in the available headers. If the kernel has BTF data, all kernel structs are always available. At this point you understand much of bpftrace, and can begin to use and write powerful one-liners. See the [Manual](../man/adoc/bpftrace.adoc) for more capabilities. bpftrace-0.23.2/docs/tutorial_one_liners_chinese.md000066400000000000000000000400111477746507000224310ustar00rootroot00000000000000# bpftrace一行教程 该教程通过12个简å•å°èŠ‚å¸®åŠ©ä½ äº†è§£bpftrace的使用。æ¯ä¸€å°èŠ‚éƒ½æ˜¯ä¸€è¡Œçš„å‘½ä»¤ï¼Œä½ å¯ä»¥å°è¯•è¿è¡Œå¹¶ç«‹åˆ»çœ‹åˆ°è¿è¡Œæ•ˆæžœã€‚该教程系列用æ¥ä»‹ç»bpftrace的概念。关于bpftrace的完整å‚考,è§[bpftrace手册](../man/adoc/bpftrace.adoc)。 该教程贡献者是Brendan Gregg, Netflix (2018), 基于他的FreeBSD DTrace教程系列[DTrace Tutorial](https://wiki.freebsd.org/DTrace/Tutorial)。 # 1. 列出所有探针 ``` bpftrace -l 'tracepoint:syscalls:sys_enter_*' ``` "bpftrace -l" 列出所有探针,并且å¯ä»¥æ·»åŠ æœç´¢é¡¹ã€‚ - 探针是用于æ•获事件数æ®çš„æ£€æµ‹ç‚¹ã€‚ - æœç´¢è¯æ”¯æŒé€šé…符,如`*`å’Œ`?`。 - "bpftrace -l" 也å¯ä»¥é€šè¿‡ç®¡é“传递给grepï¼Œè¿›è¡Œå®Œæ•´çš„æ­£åˆ™è¡¨è¾¾å¼æœç´¢ã€‚ # 2. Hello World ``` # bpftrace -e 'BEGIN { printf("hello world\n"); }' Attaching 1 probe... hello world ^C ``` æ‰“å°æ¬¢è¿Žæ¶ˆæ¯ã€‚è¿è¡ŒåŽ, 按Ctrl-C结æŸã€‚ - `BEGIN`是一个特殊的探针,在程åºå¼€å§‹æ—¶è§¦å‘探针执行(类似awkçš„BEGIN)。你å¯ä»¥ä½¿ç”¨å®ƒè®¾ç½®å˜é‡å’Œæ‰“å°æ¶ˆæ¯å¤´ã€‚ - 探针å¯ä»¥å…³è”动作,把动作放到{}中。这个例å­ä¸­ï¼ŒæŽ¢é’ˆè¢«è§¦å‘时会调用printf()。 # 3. 文件打开 ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args.filename)); }' Attaching 1 probe... snmp-pass /proc/cpuinfo snmp-pass /proc/stat snmpd /proc/net/dev snmpd /proc/net/if_inet6 ^C ``` 这里我们在文件打开的时候打å°è¿›ç¨‹å和文件å。 - 该命令以`tracepoint:syscalls:sys_enter_openat`开始: 这是tracepoint探针类型(å†…æ ¸é™æ€è·Ÿè¸ª),当进入`openat()`系统调用时执行该探针。相比kprobes探针(内核动æ€è·Ÿè¸ªï¼Œåœ¨ç¬¬6节介ç»),我们更加喜欢用tracepoints探针,因为tracepoints有稳定的应用程åºç¼–程接å£ã€‚注æ„:现代linux系统(glibc >= 2.26),`open`总是调用`openat`系统调用。 - `comm`是内建å˜é‡ï¼Œä»£è¡¨å½“å‰è¿›ç¨‹çš„å字。其它类似的å˜é‡è¿˜æœ‰pidå’Œtid,分别表示进程标识和线程标识。 - `args`æ˜¯ä¸€ä¸ªåŒ…å«æ‰€æœ‰tracepoint傿•°çš„结构。这个结构是由bpftraceæ ¹æ®tracepointä¿¡æ¯è‡ªåŠ¨ç”Ÿæˆçš„。这个结构的æˆå‘˜å¯ä»¥é€šè¿‡å‘½ä»¤`bpftrace -vl tracepoint:syscalls:sys_enter_openat`找到。 - `args.filename`用æ¥èŽ·å–argsçš„æˆå‘˜å˜é‡`filename`的值。 - `str()`ç”¨æ¥æŠŠå­—ç¬¦ä¸²æŒ‡é’ˆè½¬æ¢æˆå­—符串。 # 4. 进程级系统调用计数 ``` bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' Attaching 1 probe... ^C @[bpftrace]: 6 @[systemd]: 24 @[snmp-pass]: 96 @[sshd]: 125 ``` 按Ctrl-CåŽæ‰“å°è¿›ç¨‹çš„系统调用计数。 - @: 表示一ç§ç‰¹æ®Šçš„å˜é‡ç±»åž‹ï¼Œç§°ä¸ºmap,å¯ä»¥ä»¥ä¸åŒçš„æ–¹å¼æ¥å­˜å‚¨å’Œæè¿°æ•°æ®ã€‚ä½ å¯ä»¥åœ¨@åŽæ·»åŠ å¯é€‰çš„å˜é‡å(如@num),用æ¥å¢žåŠ å¯è¯»æ€§æˆ–者区分ä¸åŒçš„map。 - []: å¯é€‰çš„中括å·å…许设置map的关键字,比较åƒå…³è”数组。 - count(): 这是一个map函数 - 记录被调用次数。因为调用次数根æ®commä¿å­˜åœ¨map里,输出结果是进程执行系统调用的次数统计。 Maps会在bpftrace结æŸ(如按Ctrl-C)时自动打å°å‡ºæ¥ã€‚ # 5. read()返回值分布统计 ``` # bpftrace -e 'tracepoint:syscalls:sys_exit_read /pid == 18644/ { @bytes = hist(args.ret); }' Attaching 1 probe... ^C @bytes: [0, 1] 12 |@@@@@@@@@@@@@@@@@@@@ | [2, 4) 18 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 30 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64, 128) 19 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [128, 256) 1 |@ ``` 这里统计进程å·ä¸º18644的进程执行内核函数sys_read()的返回值,并打å°å‡ºç›´æ–¹å›¾ã€‚ - /.../: 这里设置一个过滤æ¡ä»¶(æ¡ä»¶åˆ¤æ–­),满足该过滤æ¡ä»¶æ—¶æ‰æ‰§è¡Œ{}里é¢çš„动作。在这个例å­ä¸­æ„æ€æ˜¯åªè¿½è¸ªè¿›ç¨‹å·ä¸º18644的进程。过滤æ¡ä»¶è¡¨è¾¾å¼ä¹Ÿæ”¯æŒå¸ƒå°”è¿ç®—,如("&&", "||")。 - ret: 表示函数的返回值。对于sys_read(),它å¯èƒ½æ˜¯-1(错误)或者æˆåŠŸè¯»å–的字节数。 - @: 类似于上节的map,但是这里没有key,å³[]。该mapçš„åç§°"bytes"会出现在输出中。 - hist(): 一个mapå‡½æ•°ï¼Œç”¨æ¥æè¿°ç›´æ–¹å›¾çš„å‚æ•°ã€‚输出行以2次方的间隔开始,如`[128, 256)`表示值大于等于128且å°äºŽ256。åŽé¢è·Ÿç€ä½äºŽè¯¥åŒºé—´çš„傿•°ä¸ªæ•°ç»Ÿè®¡ï¼Œæœ€åŽæ˜¯asciiç è¡¨ç¤ºçš„直方图。该图å¯ä»¥ç”¨æ¥ç ”究它的模å¼åˆ†å¸ƒã€‚ - 其它的map函数还有lhist(线性直方图),count(),sum(),avg(),min()å’Œmax()。 # 6. 内核动æ€è·Ÿè¸ªread()返回的字节数 ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = lhist(retval, 0, 2000, 200); }' Attaching 1 probe... ^C @bytes: (...,0] 0 | | [0, 200) 66 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [200, 400) 2 |@ | [400, 600) 3 |@@ | [600, 800) 0 | | [800, 1000) 5 |@@@ | [1000, 1200) 0 | | [1200, 1400) 0 | | [1400, 1600) 0 | | [1600, 1800) 0 | | [1800, 2000) 0 | | [2000,...) 39 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ``` 使用内核动æ€è·Ÿè¸ªæŠ€æœ¯æ˜¾ç¤ºread()返回字节数的直方图。 - `kretprobe:vfs_read`: 这是kretprobe类型(动æ€è·Ÿè¸ªå†…核函数返回值)的探针,跟踪`vfs_read`内核函数。此外还有kprobe类型的探针(在下一节介ç»)用于跟踪内核函数的调用。它们是功能强大的探针类型,让我们å¯ä»¥è·Ÿè¸ªæˆåƒä¸Šä¸‡çš„内核函数。然而它们是"ä¸ç¨³å®š"的探针类型:由于它们å¯ä»¥è·Ÿè¸ªä»»æ„内核函数,对于ä¸åŒçš„内核版本,kprobeå’Œkretprobeä¸ä¸€å®šèƒ½å¤Ÿæ­£å¸¸å·¥ä½œã€‚因为内核函数åï¼Œå‚æ•°ï¼Œè¿”回值和作用等å¯èƒ½ä¼šå˜åŒ–。此外,由于它们用æ¥è·Ÿè¸ªåº•å±‚å†…æ ¸çš„ï¼Œä½ éœ€è¦æµè§ˆå†…æ ¸æºä»£ç ï¼Œç†è§£è¿™äº›æŽ¢é’ˆçš„傿•°å’Œè¿”回值的æ„义。 - lhist(): 线性直方图函数:傿•°åˆ†åˆ«æ˜¯value,最å°å€¼ï¼Œæœ€å¤§å€¼ï¼Œæ­¥è¿›å€¼ã€‚ç¬¬ä¸€ä¸ªå‚æ•°(`retval`)表示系统调用sys_read()返回值:峿ˆåŠŸè¯»å–的字节数。 # 7. read()调用的时间 ``` # bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ { @ns[comm] = hist(nsecs - @start[tid]); delete(@start, tid); }' Attaching 2 probes... [...] @ns[snmp-pass]: [0, 1] 0 | | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 27 |@@@@@@@@@ | [512, 1k) 125 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [1k, 2k) 22 |@@@@@@@ | [2k, 4k) 1 | | [4k, 8k) 10 |@@@ | [8k, 16k) 1 | | [16k, 32k) 3 |@ | [32k, 64k) 144 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64k, 128k) 7 |@@ | [128k, 256k) 28 |@@@@@@@@@@ | [256k, 512k) 2 | | [512k, 1M) 3 |@ | [1M, 2M) 1 | | ``` æ ¹æ®è¿›ç¨‹åï¼Œä»¥ç›´æ–¹å›¾çš„å½¢å¼æ˜¾ç¤ºread()调用花费的时间,时间å•ä½ä¸ºçº³ç§’。 - @start[tid]: 使用线程ID作为key。æŸä¸€æ—¶åˆ»ï¼Œå¯èƒ½æœ‰è®¸è®¸å¤šå¤šçš„read调用正在进行,我们希望为æ¯ä¸ªè°ƒç”¨è®°å½•一个起始时间戳。这è¦å¦‚何åšåˆ°å‘¢ï¼Ÿæˆ‘们å¯ä»¥ä¸ºæ¯ä¸ªread调用建立一个唯一的标识符,并用它作为key进行统计。由于内核线程一次åªèƒ½æ‰§è¡Œä¸€ä¸ªç³»ç»Ÿè°ƒç”¨ï¼Œæˆ‘们å¯ä»¥ä½¿ç”¨çº¿ç¨‹ID作为上述标识符。 - nsecs: 自系统å¯åŠ¨åˆ°çŽ°åœ¨çš„çº³ç§’æ•°ã€‚è¿™æ˜¯ä¸€ä¸ªé«˜ç²¾åº¦æ—¶é—´æˆ³ï¼Œå¯ä»¥ç”¨æ¥å¯¹äº‹ä»¶è®¡æ—¶ã€‚ - /@start[tid]/: 该过滤æ¡ä»¶æ£€æŸ¥èµ·å§‹æ—¶é—´æˆ³æ˜¯å¦è¢«è®°å½•。程åºå¯èƒ½åœ¨æŸæ¬¡read调用中途被å¯åŠ¨ï¼Œå¦‚æžœæ²¡æœ‰è¿™ä¸ªè¿‡æ»¤æ¡ä»¶ï¼Œè¿™ä¸ªè°ƒç”¨çš„æ—¶é—´ä¼šè¢«ç»Ÿè®¡ä¸ºnow-zeroï¼Œè€Œä¸æ˜¯now-start。 - delete(@start, tid): 释放å˜é‡ã€‚ # 8. 统计进程级别的事件 ``` # bpftrace -e 'tracepoint:sched:sched* { @[probe] = count(); } interval:s:5 { exit(); }' Attaching 25 probes... @[tracepoint:sched:sched_wakeup_new]: 1 @[tracepoint:sched:sched_process_fork]: 1 @[tracepoint:sched:sched_process_exec]: 1 @[tracepoint:sched:sched_process_exit]: 1 @[tracepoint:sched:sched_process_free]: 2 @[tracepoint:sched:sched_process_wait]: 7 @[tracepoint:sched:sched_wake_idle_without_ipi]: 53 @[tracepoint:sched:sched_stat_runtime]: 212 @[tracepoint:sched:sched_wakeup]: 253 @[tracepoint:sched:sched_waking]: 253 @[tracepoint:sched:sched_switch]: 510 ``` 这里统计5秒内进程级的事件并打å°ã€‚ - sched: `sched`探针å¯ä»¥æŽ¢æµ‹è°ƒåº¦å™¨çš„高级事件和进程事件如fork, exec和上下文切æ¢ã€‚ - probe: 探针的完整å称。 - interval:s:5: 这是一个æ¯5秒在æ¯ä¸ªCPU上触å‘一次的探针,它用æ¥åˆ›å»ºè„šæœ¬çº§åˆ«çš„é—´éš”æˆ–è¶…æ—¶æ—¶é—´ã€‚ - exit(): 退出bpftrace。 # 9. 分æžå†…核实时函数栈 ``` # bpftrace -e 'profile:hz:99 { @[kstack] = count(); }' Attaching 1 probe... ^C [...] @[ filemap_map_pages+181 __handle_mm_fault+2905 handle_mm_fault+250 __do_page_fault+599 async_page_fault+69 ]: 12 [...] @[ cpuidle_enter_state+164 do_idle+390 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 22122 ``` 以99赫兹的频率分æžå†…æ ¸è°ƒç”¨æ ˆå¹¶æ‰“å°æ¬¡æ•°ç»Ÿè®¡ã€‚ - profile:hz:99: 这里所有cpu都以99赫兹的频率采样分æžå†…核栈。为什么是99è€Œä¸æ˜¯100或者1000ï¼Ÿæˆ‘ä»¬æƒ³è¦æŠ“å–足够详细的内核执行时内核栈信æ¯ï¼Œä½†æ˜¯é¢‘çŽ‡å¤ªå¤§å½±å“æ€§èƒ½ã€‚100èµ«å…¹è¶³å¤Ÿäº†ï¼Œä½†æ˜¯æˆ‘ä»¬ä¸æƒ³ç”¨æ­£å¥½100赫兹,这样采样频率å¯èƒ½ä¸Žå…¶ä»–定时事件步调一致,所以99èµ«å…¹æ˜¯ä¸€ä¸ªç†æƒ³çš„选择。 - kstack: 返回内核调用栈。这里作为map的关键字,å¯ä»¥è·Ÿè¸ªæ¬¡æ•°ã€‚这些输出信æ¯å¯ä»¥ä½¿ç”¨ç«ç„°å›¾å¯è§†åŒ–。此外`ustack`用æ¥åˆ†æžç”¨æˆ·çº§å †æ ˆã€‚ # 10. 调度器跟踪 ``` # bpftrace -e 'tracepoint:sched:sched_switch { @[kstack] = count(); }' ^C [...] @[ __schedule+697 __schedule+697 schedule+50 schedule_timeout+365 xfsaild+274 kthread+248 ret_from_fork+53 ]: 73 @[ __schedule+697 __schedule+697 schedule_idle+40 do_idle+356 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 305 ``` è¿™é‡Œç»Ÿè®¡è¿›ç¨‹ä¸Šä¸‹æ–‡åˆ‡æ¢æ¬¡æ•°ã€‚以上输出被截断,åªè¾“出了最åŽä¸¤ä¸ªç»“果。 - sched: 跟踪调度类别的调度器事件:sched_switch, sched_wakeup, sched_migrate_task等。 - sched_switch: 当线程释放cpu资æºï¼Œå½“å‰ä¸è¿è¡Œæ—¶è§¦å‘。这里å¯èƒ½çš„阻塞事件:如等待I/O,定时器,分页/交æ¢ï¼Œé”等。 - kstack: 内核堆栈跟踪,打å°è°ƒç”¨æ ˆã€‚ - sched_switch在线程切æ¢çš„æ—¶å€™è§¦å‘,打å°çš„调用栈是被切æ¢å‡ºcpu的那个线程。åƒä½ ä½¿ç”¨å…¶ä»–探针一样,注æ„这里的上下文,例如comm, pid, kstack等等,并ä¸ä¸€å®šå映了探针的目标的状æ€ã€‚ # 11. å—级I/O跟踪 ``` # bpftrace -e 'tracepoint:block:block_rq_issue { @ = hist(args.bytes); }' Attaching 1 probe... ^C @: [0, 1] 1 |@@ | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 0 | | [4K, 8K) 24 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 2 |@@@@ | [16K, 32K) 6 |@@@@@@@@@@@@@ | [32K, 64K) 5 |@@@@@@@@@@ | [64K, 128K) 0 | | [128K, 256K) 1 |@@ | ``` 以上是å—I/O请求字节数的直方图。 - tracepoint:block: å—类别的跟踪点跟踪å—级I/O事件。 - block_rq_issue: 当I/Oæäº¤åˆ°å—设备时触å‘。 - args.bytes: 跟踪点block_rq_issueçš„å‚æ•°æˆå‘˜bytes,表示æäº¤I/O请求的字节数。 该探针的上下文是éžå¸¸é‡è¦çš„: 它在I/O请求被æäº¤ç»™å—设备时触å‘。这通常å‘生在进程上下文,此时通过内核的commå¯ä»¥å¾—到进程å;也å¯èƒ½å‘生在内核上下文,(如readahead),此时ä¸èƒ½æ˜¾ç¤ºé¢„期的进程å·å’Œè¿›ç¨‹åä¿¡æ¯ã€‚ # 12. 内核结构跟踪 ``` # cat path.bt #ifndef BPFTRACE_HAVE_BTF #include #include #endif kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } # bpftrace path.bt Attaching 1 probe... open path: dev open path: if_inet6 open path: retrans_time_ms [...] ``` 这里使用内核动æ€è·Ÿè¸ªæŠ€æœ¯è·Ÿè¸ªvfs_read()函数,该函数的(struct path *)ä½œä¸ºç¬¬ä¸€ä¸ªå‚æ•°ã€‚ - kprobe: 如å‰é¢æ‰€è¿°ï¼Œè¿™æ˜¯å†…核动æ€è·Ÿè¸ªkprobe探针类型,跟踪内核函数的调用(kretprobe探针类型跟踪内核函数返回值)。 - `arg0` 是一个内建å˜é‡ï¼Œè¡¨ç¤ºæŽ¢é’ˆçš„ç¬¬ä¸€ä¸ªå‚æ•°ï¼Œå…¶å«ä¹‰ç”±æŽ¢é’ˆç±»åž‹å†³å®šã€‚对于`kprobe`ç±»åž‹æŽ¢é’ˆï¼Œå®ƒè¡¨ç¤ºå‡½æ•°çš„ç¬¬ä¸€ä¸ªå‚æ•°ã€‚å…¶å®ƒå‚æ•°ä½¿ç”¨arg1,...,argN访问。 - `((struct path *)arg0)->dentry->d_name.name`: 这里`arg0`作为`struct path *`并引用dentry。 - #include: 在没有BTF (BPF Type Format) 的情况下,包å«å¿…è¦çš„pathå’Œdentry类型声明的头文件。 bpftrace对内核结构跟踪的支æŒå’Œbcc是一样的,å…许使用内核头文件。这æ„味ç€å¤§å¤šæ•°ç»“构是å¯ç”¨çš„ï¼Œä½†æ˜¯å¹¶ä¸æ˜¯æ‰€æœ‰çš„ï¼Œæœ‰æ—¶éœ€è¦æ‰‹åŠ¨å¢žåŠ æŸäº›ç»“构的声明。例如这个例å­ï¼Œè§[dcsnoop tool](../tools/dcsnoop.bt),包å«struct nameidata的声明。倘若内核有æä¾›BTFæ•°æ®ï¼Œåˆ™æ‰€æœ‰ç»“构都å¯ç”¨ã€‚ 现在,你已ç»ç†è§£äº†bpftrace的大部分功能,你å¯ä»¥å¼€å§‹ä½¿ç”¨å’Œç¼–写强大的一行命令。查阅[使用说明书](../man/adoc/bpftrace.adoc)更多的功能。 bpftrace-0.23.2/docs/tutorial_one_liners_japanese.md000066400000000000000000000527541477746507000226220ustar00rootroot00000000000000# bpftrace ワンライナーãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ« 12個ã®ç°¡å˜ãªãƒ¬ãƒƒã‚¹ãƒ³ã§ Linux ã® bpftrace ã‚’å­¦ã³ã¾ã—ょã†ï¼Žå„レッスンã¯ãƒ¯ãƒ³ãƒ©ã‚¤ãƒŠãƒ¼ã§ã™ï¼Žã™ãã«è©¦ã™ã“ã¨ãŒã§ã,一連ã®ãƒ¯ãƒ³ãƒ©ã‚¤ãƒŠãƒ¼ã§ bpftrace ã®è¦ç‚¹ãŒåˆ†ã‹ã‚Šã¾ã™ï¼Žbpftrace ã®è©³ç´°ã¯[インストラクションマニュアル](../man/adoc/bpftrace.adoc)ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„. - 執筆:Brendan Gregg, Netflix (2018).FreeBSD [DTrace Tutorial](https://wiki.freebsd.org/DTrace/Tutorial)(Brendan Gregg 著)ã«åŸºã¥ã. - 原文:[The bpftrace One-Liner Tutorial](https://github.com/bpftrace/bpftrace/blob/master/docs/tutorial_one_liners.md) # レッスン 1. プローブã®è¡¨ç¤º ``` bpftrace -l 'tracepoint:syscalls:sys_enter_*' ``` "bpftrace -l" ã¯å…¨ã¦ã®ãƒ—ローブを表示ã—ã¾ã™ï¼Žå¾Œã‚ã«æ¤œç´¢èªžã‚’付ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Ž - プローブã¯ã‚¤ãƒ™ãƒ³ãƒˆãƒ‡ãƒ¼ã‚¿ã‚’æ•æ‰ã™ã‚‹ãŸã‚ã®è¨ˆè£…点ã§ã™ï¼Ž - 検索語ã«ã¯ãƒ¯ã‚¤ãƒ«ãƒ‰ã‚«ãƒ¼ãƒ‰ï¼ã‚°ãƒ­ãƒ–(`*`åŠã³`?`)ãŒä½¿ç”¨ã§ãã¾ã™ï¼Ž - å®Œå…¨ãªæ­£è¦è¡¨ç¾ã‚’利用ã—ãŸã„å ´åˆã¯ "bpftrace -l" ã®å‡ºåŠ›ã‚’ grep(1) ã«ãƒ‘イプã§ãã¾ã™ï¼Ž # レッスン 2. Hello World ``` # bpftrace -e 'BEGIN { printf("hello world\n"); }' Attaching 1 probe... hello world ^C ``` "hello world" を表示ã—ã¾ã™ï¼ŽCtrl-Cã§å®Ÿè¡Œã‚’終了ã—ã¾ã™ï¼Ž - `BEGIN`ã¯ç‰¹åˆ¥ãªãƒ—ローブã§ï¼Œãƒ—ログラムã®é–‹å§‹æ™‚ã«ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ã¾ã™ï¼ˆawk ã® BEGIN ã¨åŒæ§˜ã§ã™ï¼‰ï¼Žå¤‰æ•°ã®åˆæœŸåŒ–やヘッダã®å‡ºåŠ›ã«åˆ©ç”¨ã§ãã¾ã™ï¼Ž - { } ã®ä¸­ã§ãƒ—ローブã«é–¢é€£ä»˜ã‘るアクションを定義ã—ã¾ã™ï¼Žã“ã®ä¾‹ã§ã¯ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ãŸã¨ãã« printf() を実行ã—ã¾ã™ï¼Ž # レッスン 3. ファイルã®ã‚ªãƒ¼ãƒ—ン ``` # bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }' Attaching 1 probe... snmp-pass /proc/cpuinfo snmp-pass /proc/stat snmpd /proc/net/dev snmpd /proc/net/if_inet6 ^C ``` ファイルã®ã‚ªãƒ¼ãƒ—ンã®ç™ºç”Ÿã‚’トレースã—,ãã®ã¨ãã®ãƒ—ロセスåã¨ãƒ•ァイルã®ãƒ‘スåを表示ã—ã¾ã™ï¼Ž - `tracepoint:syscalls:sys_enter_openat` 㯠tracepoint プローブ(カーãƒãƒ«ã®é™çš„トレーシング)ã§ã™ï¼Žã“れã«ã‚ˆã‚Š `openat()` システムコール呼ã³å‡ºã—時ã«ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ã¾ã™ï¼ŽTracepoint ã® API ã¯å®‰å®šæ€§ãŒä¿è¨¼ã•れã¦ã„ã‚‹ãŸã‚,kprobe(カーãƒãƒ«ã®å‹•的トレーシング,レッスン6ã§ç´¹ä»‹ï¼‰ã‚ˆã‚Šã‚‚åˆ©ç”¨ãŒæŽ¨å¥¨ã•れã¾ã™ï¼ŽãªãŠï¼Œæœ€è¿‘ã® Linux(カーãƒãƒ«2.26以上)ã§ã¯ `open` 関数ã¯å¸¸ã« `openat` システムコールを呼ã³ã¾ã™ï¼Ž - `comm` ã¯ãƒ“ルトイン変数ã®ä¸€ã¤ã§ï¼Œç¾åœ¨ã®ãƒ—ロセスåã‚’ä¿æŒã—ã¾ã™ï¼ŽåŒæ§˜ã®ãƒ“ルトイン変数㫠pid ã‚„ tid ãŒã‚りã¾ã™ï¼Ž - `args` ã¯å¯¾è±¡ã® tracepoint ã®å…¨ã¦ã®å¼•æ•°ã‚’å«ã‚€æ§‹é€ ä½“ã¸ã®ãƒã‚¤ãƒ³ã‚¿ã§ã™ï¼Žã“ã®æ§‹é€ ä½“㯠tracepoint ã®æƒ…å ±ã«åŸºã¥ã„㦠bpftrace ãŒè‡ªå‹•ã§ç”Ÿæˆã—ã¾ã™ï¼Žæ§‹é€ ä½“ã®ãƒ¡ãƒ³ãƒã®æƒ…報㯠`bpftrace -vl tracepoint:syscalls:sys_enter_openat` ã§èª¿ã¹ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Ž - `args->filename` 㯠`args` 構造体をå‚ç…§ã—ã¦ãƒ¡ãƒ³ãƒå¤‰æ•° `filename` ã®å€¤ã‚’å–å¾—ã—ã¾ã™ï¼Ž - `str()` ã¯ãƒã‚¤ãƒ³ã‚¿ã‚’文字列ã«å¤‰æ›ã—ã¾ã™ï¼Žï¼ˆè¨³æ³¨ï¼šbpftrace ã¯ãƒã‚¤ãƒ³ã‚¿ã¨æ–‡å­—列を別々ã®ã‚‚ã®ã¨ã—ã¦æ‰±ã„ã¾ã™ï¼Žprintf("%s") ã®å¼•æ•°ã«ã¯æ–‡å­—列を与ãˆã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Ž ) # レッスン 4. プロセスã”ã¨ã®ã‚·ã‚¹ãƒ†ãƒ ã‚³ãƒ¼ãƒ«å‘¼ã³å‡ºã—回数 ``` bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' Attaching 1 probe... ^C @[bpftrace]: 6 @[systemd]: 24 @[snmp-pass]: 96 @[sshd]: 125 ``` プロセスã”ã¨ã«ã‚·ã‚¹ãƒ†ãƒ ã‚³ãƒ¼ãƒ«å‘¼ã³å‡ºã—ã®å›žæ•°ã‚’計数ã—ã¾ã™ï¼Žé›†è¨ˆçµæžœã¯ Ctrl-C ã§ãƒ—ログラムを終了ã—ãŸéš›ã«è¡¨ç¤ºã•れã¾ã™ï¼Ž - @: ã“れã¯ãƒžãƒƒãƒ—ã¨å‘¼ã°ã‚Œã‚‹ç‰¹æ®Šãªå¤‰æ•°ã§ã™ï¼Žãƒžãƒƒãƒ—ã¯ã•ã¾ã–ã¾ãªæ–¹æ³•ã§ãƒ‡ãƒ¼ã‚¿ã®æ ¼ç´ã‚„集計ãŒã§ãã¾ã™ï¼Ž@ ã®å¾Œã‚ã«å¤‰æ•°å(例ãˆã° "@num")を付ã‘ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ï¼Žã“れã«ã‚ˆã‚Šå¯èª­æ€§ã‚„識別性をå‘上ã§ãã¾ã™ï¼Ž - []: マップã®å¾Œã‚ã«å¤§æ‹¬å¼§ã‚’ã¤ã‘ã‚‹ã¨ï¼Œé€£æƒ³é…列ã®ã‚ˆã†ã«ã‚­ãƒ¼ãŒæŒ‡å®šã§ãã¾ã™ï¼Žã“れã¯çœç•¥å¯èƒ½ã§ã™ï¼Ž - count(): ã“れã¯ãƒžãƒƒãƒ—ã«å¯¾ã™ã‚‹é–¢æ•°ã®ä¸€ã¤ã§ï¼Œå‘¼ã³å‡ºã•れãŸå›žæ•°ã‚’計数ã—ã¾ã™ï¼Žä»Šå›žã®å ´åˆãƒžãƒƒãƒ—ã«ã¯ comm をキーã¨ã—㦠count() ã®å€¤ãŒä¿å­˜ã•れã¾ã™ï¼Žçµæžœã¨ã—ã¦ãƒ—ロセスã”ã¨ã®ã‚·ã‚¹ãƒ†ãƒ ã‚³ãƒ¼ãƒ«å‘¼ã³å‡ºã—ã®å›žæ•°ãŒè¨ˆæ•°ã•れã¾ã™ï¼Ž ãƒžãƒƒãƒ—ã«æ ¼ç´ã•れãŸå€¤ã¯ bpftrace ãŒçµ‚了ã—ãŸã¨ã(Ctrl-Cを押ã—ãŸã¨ããªã©ï¼‰ã«è‡ªå‹•ã§è¡¨ç¤ºã•れã¾ã™ï¼Ž # レッスン 5. read() ã®ãƒã‚¤ãƒˆæ•°ã®åˆ†å¸ƒ ``` # bpftrace -e 'tracepoint:syscalls:sys_exit_read /pid == 18644/ { @bytes = hist(args->ret); }' Attaching 1 probe... ^C @bytes: [0, 1] 12 |@@@@@@@@@@@@@@@@@@@@ | [2, 4) 18 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 30 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64, 128) 19 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [128, 256) 1 |@ ``` PID 18644 ã®ãƒ—ロセスã«ã‚ˆã‚‹ã‚«ãƒ¼ãƒãƒ«é–¢æ•° sys_read() ã®æˆ»ã‚Šå€¤ã‚’ヒストグラムã«ã¾ã¨ã‚ã¾ã™ï¼Ž - /.../: ã“れã¯ãƒ•ィルタ(述語ã¨ã‚‚ã„ã†ï¼‰ã§ã™ï¼Žã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯ã“ã®ãƒ•ィルタå¼ãŒçœŸã®ã¨ãã®ã¿å®Ÿè¡Œã•れã¾ã™ï¼Žä»Šå›žã®å ´åˆã¯PIDãŒ18644ã®ã¨ãã«ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒå®Ÿè¡Œã•れã¾ã™ï¼Žãƒ•ィルタ内ã§ã¯ãƒ–ール値ã®ã‚ªãƒšãƒ¬ãƒ¼ã‚¿ãƒ¼ï¼ˆ"&&", "||")ãŒåˆ©ç”¨ã§ãã¾ã™ï¼Ž - ret: é–¢æ•°ã®æˆ»ã‚Šå€¤ã‚’æ„味ã—ã¾ã™ï¼Ž sys_read() ã®å ´åˆï¼Œæˆ»ã‚Šå€¤ã¯ -1 (エラー)ã‹ï¼Œread ã«æˆåŠŸã—ãŸãƒã‚¤ãƒˆæ•°ã§ã™ï¼Ž - @: å‰ã®ãƒ¬ãƒƒã‚¹ãƒ³ã¨åŒæ§˜ã«ãƒžãƒƒãƒ—ã§ã™ï¼Žä»Šå›žã¯ã‚­ãƒ¼ï¼ˆå¤§æ‹¬å¼§ï¼‰ãŒç„¡ã,"bytes" ã¨ã„ã†åå‰ãŒã¤ã„ã¦ã„ã¾ã™ï¼Žã“ã®åå‰ã¯æœ€å¾Œã®çµæžœã®å‡ºåŠ›ã®ã¨ãã«è¡¨ç¤ºã•れã¾ã™ï¼Ž - hist(): 底2ã®å¯¾æ•°ã‚¹ã‚±ãƒ¼ãƒ«ã®ãƒ’ストグラムã¨ã—ã¦å€¤ã‚’集計ã™ã‚‹ãƒžãƒƒãƒ—関数ã§ã™ï¼Žå‡ºåŠ›ã®è¡Œã®å…ˆé ­ã¯å€¤ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒ«ã‚’示ã—ã¾ã™ï¼Žä¾‹ãˆã°ï¼Œ`[128, 256)` ã¯å€¤ãŒ128以上256未満ã§ã‚ã‚‹ã“ã¨ã‚’æ„味ã—ã¾ã™ï¼Žãã®æ¨ªã¯ç™ºç”Ÿå›žæ•°åŠã³ï¼ŒASCII文字ã«ã‚ˆã‚‹ç™ºç”Ÿå›žæ•°ã®ãƒ’ストグラムã§ã™ï¼Žãƒ’ストグラムã¯ã®åˆ†å¸ƒã®å¤šå³°æ€§ã®èª¿æŸ»ã«æ´»ç”¨ã§ãã¾ã™ï¼Ž - ä»–ã®ãƒžãƒƒãƒ—関数ã¨ã—㦠lhist()(線形スケールã®ãƒ’ストグラム),sum(),avg(),min() ãã—㦠max() ãŒã‚りã¾ã™ï¼Ž # レッスン 6. カーãƒãƒ«å‹•的トレーシングã«ã‚ˆã‚‹ read() ã®ãƒã‚¤ãƒˆæ•°ã®é›†è¨ˆ ``` # bpftrace -e 'kretprobe:vfs_read { @bytes = lhist(retval, 0, 2000, 200); }' Attaching 1 probe... ^C @bytes: (...,0] 0 | | [0, 200) 66 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [200, 400) 2 |@ | [400, 600) 3 |@@ | [600, 800) 0 | | [800, 1000) 5 |@@@ | [1000, 1200) 0 | | [1200, 1400) 0 | | [1400, 1600) 0 | | [1600, 1800) 0 | | [1800, 2000) 0 | | [2000,...) 39 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ``` read() ã®ãƒã‚¤ãƒˆæ•°ã‚’線形スケールã®ãƒ’ストグラムã¨ã—ã¦é›†è¨ˆã—ã¾ã™ï¼Žãƒˆãƒ¬ãƒ¼ã‚¹ã«ã¯ã‚«ãƒ¼ãƒãƒ«ã®å‹•的トレーシングを利用ã—ã¾ã™ï¼Ž - `kretprobe:vfs_read` 㯠kretprobe ãƒ—ãƒ­ãƒ¼ãƒ–ï¼ˆé–¢æ•°ã®æˆ»ã‚Šã«å¯¾ã™ã‚‹ã‚«ãƒ¼ãƒãƒ«å‹•的トレーシング)を `vfs_read()` カーãƒãƒ«é–¢æ•°ã«è¨­å®šã—ã¾ã™ï¼Žé–¢æ•°ã®å®Ÿè¡Œé–‹å§‹æ™‚ã«ã‚¤ãƒ™ãƒ³ãƒˆã‚’発生ã•ã›ã‚‹ kprobe プローブ(次ã®ãƒ¬ãƒƒã‚¹ãƒ³ã§ç´¹ä»‹ï¼‰ã‚‚ã‚りã¾ã™ï¼Žã“れらã¯å¼·åŠ›ãªãƒ—ローブタイプã§ï¼Œæ•°ä¸‡ã®ç•°ãªã‚‹ã‚«ãƒ¼ãƒãƒ«é–¢æ•°ã‚’トレースã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Žã—ã‹ã—ã“れらã¯ã€Œä¸å®‰å®šã€ãªãƒ—ローブã§ã™ï¼Žãªãœãªã‚‰ï¼Œã‚«ãƒ¼ãƒãƒ«é–¢æ•°ã®å称,引数,戻り値,ãã—ã¦å½¹å‰²ã¯ã‚«ãƒ¼ãƒãƒ«ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã”ã¨ã«å¤‰ã‚ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ãŸã‚ã§ã™ï¼Žkprobe/kretprobe ãŒç•°ãªã‚‹ã‚«ãƒ¼ãƒãƒ«ã§å‹•作ã™ã‚‹ä¿è¨¼ã¯ã‚りã¾ã›ã‚“.ã¾ãŸï¼Œç”Ÿã®ã‚«ãƒ¼ãƒãƒ«é–¢æ•°ã‚’トレースã™ã‚‹ã“ã¨ã«ãªã‚‹ãŸã‚ï¼Œãƒ—ãƒ­ãƒ¼ãƒ–ã‚„å¼•æ•°ï¼Œæˆ»ã‚Šå€¤ã®æ„味をç†è§£ã™ã‚‹ãŸã‚ã«ã¯ã‚«ãƒ¼ãƒãƒ«ã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’å‚ç…§ã™ã‚‹å¿…è¦ãŒã‚ã‚‹ã§ã—ょã†ï¼Ž - lhist(): 線形スケールã®ãƒ’ストグラムを作æˆã—ã¾ã™ï¼Žå¼•数㯠値,最å°å€¤ï¼Œæœ€å¤§å€¤ï¼Œã‚¹ãƒ†ãƒƒãƒ— ã§ã™ï¼Žæœ€åˆã®å¼•数(`retval`)㯠vfs_read() ã®æˆ»ã‚Šå€¤ã§ï¼Œã“れã¯èª­ã¿å‡ºã—ãŸãƒã‚¤ãƒˆæ•°ã§ã™ï¼Ž # レッスン 7. read() ã®å®Ÿè¡Œæ™‚é–“ã®æ¸¬å®š ``` # bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ { @ns[comm] = hist(nsecs - @start[tid]); delete(@start, tid); }' Attaching 2 probes... [...] @ns[snmp-pass]: [0, 1] 0 | | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 27 |@@@@@@@@@ | [512, 1k) 125 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [1k, 2k) 22 |@@@@@@@ | [2k, 4k) 1 | | [4k, 8k) 10 |@@@ | [8k, 16k) 1 | | [16k, 32k) 3 |@ | [32k, 64k) 144 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64k, 128k) 7 |@@ | [128k, 256k) 28 |@@@@@@@@@@ | [256k, 512k) 2 | | [512k, 1M) 3 |@ | [1M, 2M) 1 | | ``` read() ã®å®Ÿè¡Œæ™‚間をナノ秒å˜ä½ã§è¨ˆæ¸¬ã—,プロセスã”ã¨ã«ãƒ’ストグラムã§é›†è¨ˆã—ã¾ã™ï¼Ž - @start[tid]: ã“れã¯ãƒžãƒƒãƒ—ã®ã‚­ãƒ¼ã«ã‚¹ãƒ¬ãƒƒãƒ‰IDを利用ã—ã¦ã„ã¾ã™ï¼Žread ã¯åŒæ™‚ã«è¤‡æ•°å®Ÿè¡Œã•れるå¯èƒ½æ€§ãŒã‚りã¾ã™ï¼Žãれãžã‚Œã®é–‹å§‹æ™‚刻をã©ã†ä¿å­˜ã™ã‚Œã°è‰¯ã„ã§ã—ょã†ã‹ï¼Ÿãれãžã‚Œã® read ã«å¯¾ã—ã¦ä¸€æ„ã®è­˜åˆ¥å­ãŒç”Ÿæˆã§ãれã°ï¼Œãれをキーã¨ã—ã¦åˆ©ç”¨ã§ãã¾ã™ï¼Žã‚るカーãƒãƒ«ã‚¹ãƒ¬ãƒƒãƒ‰ã¯ä¸€åº¦ã«ä¸€ã¤ã®ã‚·ã‚¹ãƒ†ãƒ ã‚³ãƒ¼ãƒ«ã—ã‹å®Ÿè¡Œã§ããªã„ãŸã‚,スレッドIDを一æ„ã®è­˜åˆ¥å­ã¨ã—ã¦åˆ©ç”¨ã§ãã¾ã™ï¼Ž - nsecs: マシン起動ã‹ã‚‰ã®ãƒŠãƒŽç§’ã‚’æ„味ã—ã¾ã™ï¼Žã“れã¯é«˜ç²¾åº¦ã®ã‚¿ã‚¤ãƒ ã‚¹ã‚¿ãƒ³ãƒ—カウンターã®å€¤ã§ï¼Œã‚¤ãƒ™ãƒ³ãƒˆæ™‚åˆ»ã®æ¸¬å®šã«åˆ©ç”¨ã§ãã¾ã™ï¼Ž - /@start[tid]/: ã“ã®ãƒ•ィルタã¯é–‹å§‹æ™‚é–“ãŒè¨˜éŒ²ã•れã¦ã„ã‚‹ã‹ã‚’ãƒã‚§ãƒƒã‚¯ã—ã¾ã™ï¼Žã“ã®ãƒ•ィルタãŒç„¡ã„å ´åˆï¼Œã“ã®ãƒ—ログラムã¯ã‚ã‚‹ read ã®é–‹å§‹å¾Œã«å®Ÿè¡Œã•れ,ãã® read ã®çµ‚了ã®ã‚¤ãƒ™ãƒ³ãƒˆã®ã¿ã‚’æ•æ‰ã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ï¼Žã“ã®å ´åˆï¼Œçµæžœã¨ã—㦠ç¾åœ¨æ™‚刻 - 開始時間ã§ã¯ãªã,ç¾åœ¨æ™‚刻 - 0 を計算ã™ã‚‹ã“ã¨ã«ãªã‚Šã¾ã™ï¼Žï¼ˆè¨³æ³¨ï¼šå­˜åœ¨ã—ãªã„キーã«å¯¾ã™ã‚‹ãƒžãƒƒãƒ—アクセスã¯0ã‚’è¿”ã—ã¾ã™ï¼‰ - delete(@start, tid): 変数を解放ã—ã¾ã™ï¼Žï¼ˆè¨³æ³¨ï¼šdelete ã‚’ã—ãŸãƒžãƒƒãƒ—ã®å€¤ã¯ï¼Œãƒ—ログラム終了時ã«è¡¨ç¤ºã•れã¾ã›ã‚“.) # レッスン 8. プロセスレベルã®ã‚¤ãƒ™ãƒ³ãƒˆã®è¨ˆæ•° ``` # bpftrace -e 'tracepoint:sched:sched* { @[probe] = count(); } interval:s:5 { exit(); }' Attaching 25 probes... @[tracepoint:sched:sched_wakeup_new]: 1 @[tracepoint:sched:sched_process_fork]: 1 @[tracepoint:sched:sched_process_exec]: 1 @[tracepoint:sched:sched_process_exit]: 1 @[tracepoint:sched:sched_process_free]: 2 @[tracepoint:sched:sched_process_wait]: 7 @[tracepoint:sched:sched_wake_idle_without_ipi]: 53 @[tracepoint:sched:sched_stat_runtime]: 212 @[tracepoint:sched:sched_wakeup]: 253 @[tracepoint:sched:sched_waking]: 253 @[tracepoint:sched:sched_switch]: 510 ``` 5秒間プロセスレベルã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’計数ã—,サマリを出力ã—ã¾ã™ï¼Ž - sched: tracepoint ã® `sched` プローブカテゴリã«ã¯ï¼Œfork ã‚„ exec,コンテキストスイッãƒãªã©ã®é«˜ä½ã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ©ã¨ãƒ—ロセスã«é–¢ã™ã‚‹ã‚¤ãƒ™ãƒ³ãƒˆãŒã‚りã¾ã™ï¼Ž - probe: ãƒ—ãƒ­ãƒ¼ãƒ–ã®æ­£å¼åã‚’ä¿æŒã™ã‚‹ãƒ“ルトイン変数ã§ã™ï¼Žï¼ˆè¨³æ³¨ï¼š`tracepoint:sched:sched*` ã¯ãƒžãƒƒãƒã—ãŸå…¨ã¦ã®ãƒ—ローブã«ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’ç´ä»˜ã‘ã¾ã™ï¼Žprobe を利用ã—ã¦ï¼Œå®Ÿéš›ã®ãƒ—ローブã®å称をプログラム内ã‹ã‚‰å‚ç…§ã§ãã¾ã™ï¼Žï¼‰ - interval:s:5: ã‚る一ã¤ã®CPU上ã§5ç§’é–“ã«ä¸€åº¦å®Ÿè¡Œã•れるプローブã§ã™ï¼Žã‚¹ã‚¯ãƒªãƒ—トã«ã‚ˆã‚‹ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒ«ã‚ã‚‹ã„ã¯ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã‚¤ãƒ™ãƒ³ãƒˆã®ä½œæˆã«åˆ©ç”¨ã§ãã¾ã™ï¼Ž - exit(): bpftrace を終了ã—ã¾ã™ï¼Ž # レッスン 9. CPU上ã®ã‚«ãƒ¼ãƒãƒ«ã‚¹ã‚¿ãƒƒã‚¯ã®ãƒ—ロファイリング ``` # bpftrace -e 'profile:hz:99 { @[kstack] = count(); }' Attaching 1 probe... ^C [...] @[ filemap_map_pages+181 __handle_mm_fault+2905 handle_mm_fault+250 __do_page_fault+599 async_page_fault+69 ]: 12 [...] @[ cpuidle_enter_state+164 do_idle+390 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 22122 ``` 99ヘルツã§ã‚«ãƒ¼ãƒãƒ«ã‚¹ã‚¿ãƒƒã‚¯ã®ãƒ—ロファイリングをãŠã“ãªã„,ãã®å‡ºç¾é »åº¦ã‚’出力ã—ã¾ã™ï¼Ž - profile:hz:99: å…¨ã¦ã®CPU上ã§99ヘルツã§ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ã¾ã™ï¼Žä½•æ•…100ã‚„1000ã§ã¯ãªã99ã§ã—ょã†ã‹ï¼Ÿãƒ—ロファイル頻度ã¯å®Ÿè¡Œã‚’俯瞰的ã«ã‚‚局所的ã«ã‚‚æ‰ãˆã‚‹ã“ã¨ãŒã§ãã‚‹ã»ã©å分ã‹ã¤ï¼Œãƒ‘フォーマンスを乱ã•ãªã„程度ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Ž100ヘルツã¯å分ãªé »åº¦ã§ã™ãŒï¼Œ100ã®å ´åˆä»–ã®ã‚¿ã‚¤ãƒžã«ã‚ˆã‚‹ãƒ­ãƒƒã‚¯é–“éš”ã¨åŒã˜é »åº¦ã§ã‚µãƒ³ãƒ—リングã•れるå¯èƒ½æ€§ãŒã‚りã¾ã™ï¼Žãã“ã§99を利用ã—ã¾ã™ï¼Ž - kstack: カーãƒãƒ«ã®ã‚¹ã‚¿ãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹ã‚’è¿”ã—ã¾ã™ï¼Žã“れã¯ãƒžãƒƒãƒ—ã®ã‚­ãƒ¼ã¨ã—ã¦åˆ©ç”¨å¯èƒ½ã§ï¼Œcount() ã¨åˆã‚ã›ã¦é »åº¦ã®è¨ˆæ•°ãŒã§ãã¾ã™ï¼Žã“ã®å‡ºåŠ›ã¯ flame graph ã¨ã—ã¦å¯è¦–化ã™ã‚‹ã®ã«æœ€é©ã§ã™ï¼Žã¾ãŸï¼Œãƒ¦ãƒ¼ã‚¶ãƒ¬ãƒ™ãƒ«ã®ã‚¹ã‚¿ãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹ç”¨ã« `ustack` ãŒã‚りã¾ã™ï¼Ž # Lesson 10. スケジューラã®ãƒˆãƒ¬ãƒ¼ã‚·ãƒ³ã‚° ``` # bpftrace -e 'tracepoint:sched:sched_switch { @[kstack] = count(); }' ^C [...] @[ __schedule+697 __schedule+697 schedule+50 schedule_timeout+365 xfsaild+274 kthread+248 ret_from_fork+53 ]: 73 @[ __schedule+697 __schedule+697 schedule_idle+40 do_idle+356 cpu_startup_entry+111 start_secondary+423 secondary_startup_64+165 ]: 305 ``` コンテキストスイッãƒï¼ˆoff-CPU)イベントã«ç¹‹ãŒã‚‹ã‚¹ã‚¿ãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹ã‚’計数ã—ã¾ã™ï¼Žä¸Šè¨˜ã¯å‡ºåŠ›ã®æœ€å¾Œã®äºŒã¤ã®ã¿ã‚’表示ã—ã¦ã„ã¾ã™ï¼Ž - sched: tracepoint ã® sched カテゴリã«ã¯ã‚«ãƒ¼ãƒãƒ«ã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ©ã‚¤ãƒ™ãƒ³ãƒˆã«é–¢ã™ã‚‹ tracepoint ãŒè¤‡æ•°ã‚りã¾ã™ï¼Žsched_switch, sched_wakeup, sched_migrate_task ãªã©ï¼Ž - sched_switch: ã“ã®ãƒ—ローブã¯ã‚¹ãƒ¬ãƒƒãƒ‰ãŒ CPU ã‹ã‚‰é›¢ã‚Œã‚‹æ™‚ã«ç™ºç”Ÿã—ã¾ã™ï¼Žã“れ㯠I/O å¾…ã¡ã‚„タイマ,ページング/スワッピング,ロックãªã©ã®ãƒ–ロッキングイベントã®ã¨ãã«èµ·ã“りã¾ã™ï¼Ž - kstack: カーãƒãƒ«ã®ã‚¹ã‚¿ãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹ã§ã™ï¼Ž - sched_switch ã¯ã‚¹ãƒ¬ãƒƒãƒ‰ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆå†…ã§ç™ºç”Ÿã—ã¾ã™ï¼Žãã®ãŸã‚スタックã¯CPUã‹ã‚‰é›¢ã‚Œã‚‹ã‚¹ãƒ¬ãƒƒãƒ‰ã®ã‚‚ã®ã§ã™ï¼Žä»–ã®ãƒ—ローブタイプを利用ã™ã‚‹ã¨ãã¯ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã«æ³¨æ„を払ã†å¿…è¦ãŒã‚りã¾ã™ï¼Žcomm ã‚„ pid, kstack ãªã©ã¯ãƒ—ローブã®å¯¾è±¡ã‚’å‚ç…§ã—ã¦ã„ãªã„å ´åˆãŒã‚りã¾ã™ï¼Ž # Lesson 11. ブロック I/O ã®ãƒˆãƒ¬ãƒ¼ã‚·ãƒ³ã‚° ``` # bpftrace -e 'tracepoint:block:block_rq_issue { @ = hist(args->bytes); }' Attaching 1 probe... ^C @: [0, 1] 1 |@@ | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 0 | | [4K, 8K) 24 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 2 |@@@@ | [16K, 32K) 6 |@@@@@@@@@@@@@ | [32K, 64K) 5 |@@@@@@@@@@ | [64K, 128K) 0 | | [128K, 256K) 1 |@@ | ``` ブロックI/Oè¦æ±‚ã‚’ãƒã‚¤ãƒˆå˜ä½ã§ãƒ’ストグラムã¨ã—ã¦è¡¨ç¤ºã—ã¾ã™ï¼Ž - tracepoint:block: tracepoint ã® block ã‚«ãƒ†ã‚´ãƒªã¯æ§˜ã€…ãªãƒ–ロックI/O(ストレージ)イベントをトレースã—ã¾ã™ï¼Ž - block_rq_issue: デãƒã‚¤ã‚¹ã« I/O ãŒç™ºè¡Œã•ã‚ŒãŸæ™‚ã«ã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ã¾ã™ï¼Ž - args->bytes: Tracepoint ã® block_rq_issue ã®å¼•æ•°ã®ãƒ¡ãƒ³ãƒå¤‰æ•°ã§ï¼Œãƒ–ロックI/Oã®ã‚µã‚¤ã‚ºã‚’ãƒã‚¤ãƒˆå˜ä½ã§è¡¨ã—ã¾ã™ï¼Ž ã“ã®ãƒ—ローブã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã¯é‡è¦ã§ã™ï¼Žã“ã®ã‚¤ãƒ™ãƒ³ãƒˆã¯ãƒ‡ãƒã‚¤ã‚¹ã«å¯¾ã—㦠I/O ãŒç™ºè¡Œã•れãŸã¨ãã«ç™ºç”Ÿã—ã¾ã™ï¼Žã“れã¯ã‚ˆãプロセスコンテキストã§ç™ºç”Ÿã—,ãã®å ´åˆãƒ“ルトイン変数,例ãˆã° comm ã¯ãã®ã¨ãã®ãƒ—ロセスåã‚’æ„味ã—ã¾ã™ãŒï¼Œã“ã®ã‚¤ãƒ™ãƒ³ãƒˆã¯ã‚«ãƒ¼ãƒãƒ«ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆï¼ˆä¾‹ãˆã° readahead)ã§ã‚‚発生ã—ã¾ã™ï¼Žã“ã®å ´åˆ pid ã‚„ comm ã¯äºˆæœŸã—ãªã„ã‚‚ã®ã«ãªã‚‹ã§ã—ょã†ï¼Ž # Lesson 12. カーãƒãƒ«æ§‹é€ ä½“ã®ãƒˆãƒ¬ãƒ¼ã‚·ãƒ³ã‚° ``` # cat path.bt #ifndef BPFTRACE_HAVE_BTF #include #include #endif kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } # bpftrace path.bt Attaching 1 probe... open path: dev open path: if_inet6 open path: retrans_time_ms [...] ``` カーãƒãƒ«ã®å‹•的トレーシング㧠vfs_open() をトレーシングã—ã¾ã™ï¼Žã“ã®é–¢æ•°ã¯ (struct path *) を第一引数ã«å–りã¾ã™ï¼Ž - kprobe: 以å‰èª¬æ˜Žã—ãŸã‚ˆã†ã«ï¼Œã“れã¯ã‚«ãƒ¼ãƒãƒ«ã®å‹•的トレーシングをãŠã“ãªã†ãƒ—ローブタイプã§ï¼Œã‚«ãƒ¼ãƒãƒ«é–¢æ•°ã®é–‹å§‹ã‚’トレースã—ã¾ã™ï¼ˆé–¢æ•°ã‹ã‚‰ã®æˆ»ã‚Šã®ãƒˆãƒ¬ãƒ¼ã‚¹ã«ã¯ kretprobe を利用ã—ã¾ã™ï¼‰ï¼Ž - `arg0` ã¯ãƒ“ルトイン変数ã§ï¼Œãƒ—ãƒ­ãƒ¼ãƒ–ã®æœ€åˆã®å¼•æ•°ã‚’æ„味ã—ã¾ã™ï¼Žã“れã¯ãƒ—ローブタイプã”ã¨ã«æ„味ãŒç•°ãªã‚Šï¼Œ`kprobe` ã®å ´åˆã¯é–¢æ•°ã®æœ€åˆã®å¼•æ•°ã‚’æ„味ã—ã¾ã™ï¼Žä»–ã®å¼•æ•°ã«ã¯ arg1, ..., argN ã§ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ï¼Ž - `((struct path *)arg0)->dentry->d_name.name`: `arg0` ã‚’ `struct path *` ã«ã‚­ãƒ£ã‚¹ãƒˆã—ã¦ã‹ã‚‰ dentry や後続ã®ãƒ¡ãƒ³ãƒå¤‰æ•°ã‚’å‚ç…§ã—ã¾ã™ï¼Ž - #include: BTF (BPF Type Format) ã‚’æŒãŸãªã„システム㧠path ãŠã‚ˆã³ dentry 構造定義ã«å¿…è¦ãªãƒ•ァイルをインクルードã—ã¾ã™ã€‚ カーãƒãƒ«æ§‹é€ ä½“ã®ã‚µãƒãƒ¼ãƒˆã¯ bcc ã¨åŒæ§˜ã«ã‚«ãƒ¼ãƒãƒ«ãƒ˜ãƒƒãƒ€ã‚’利用ã—ã¾ã™ï¼Žã—ãŸãŒã£ã¦å¤šãã®æ§‹é€ ä½“ãŒåˆ©ç”¨å¯èƒ½ã§ã™ãŒï¼Œå…¨ã¦ã§ã¯ã‚りã¾ã›ã‚“.場åˆã«ã‚ˆã£ã¦ã¯æ‰‹å‹•ã§æ§‹é€ ä½“を定義ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Žä¾‹ãˆã° [dcsnoop tool](../tools/dcsnoop.bt) ã§ã¯ nameidata 構造体ã®ä¸€éƒ¨ã‚’手動ã§å®šç¾©ã—ã¦ã„ã¾ã™ï¼Žã“れã¯ã“ã®æ§‹é€ ä½“ãŒãƒ˜ãƒƒãƒ€å†…ã§å®šç¾©ã•れã¦ã„ãªã„ãŸã‚ã§ã™ï¼ŽLinuxカーãƒãƒ«ã®BTFデータãŒã‚ã‚‹å ´åˆï¼Œå…¨ã¦ã®æ§‹é€ ä½“ãŒåˆ©ç”¨å¯èƒ½ã§ã™ï¼Ž ã“ã“ã¾ã§ã§ bpftrace ã®å¤šãã‚’ç†è§£ã—,強力ãªãƒ¯ãƒ³ãƒ©ã‚¤ãƒŠãƒ¼ã‚’作æˆãƒ»åˆ©ç”¨ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Žbpftrace ã®ãã®ä»–ã®æ©Ÿèƒ½ã«ã¤ã„ã¦ã¯ [インストラクションマニュアル](../man/adoc/bpftrace.adoc) ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„. bpftrace-0.23.2/flake.lock000066400000000000000000000111601477746507000153400ustar00rootroot00000000000000{ "nodes": { "appimage-runtime": { "flake": false, "locked": { "lastModified": 1733518164, "narHash": "sha256-htRRxzQuzzYrIFRskLyVFR3brkJUVhuVLhjd/P5JoQw=", "owner": "danobi", "repo": "appimage-runtime", "rev": "23f655a9313a6b962e072f12534982b925ecb8f7", "type": "github" }, "original": { "owner": "danobi", "repo": "appimage-runtime", "rev": "23f655a9313a6b962e072f12534982b925ecb8f7", "type": "github" } }, "blazesym": { "flake": false, "locked": { "lastModified": 1739543304, "narHash": "sha256-9kJdqi1lCi+rk/X4/9O0foo+tJf9tcYS4tTKcu/ICFU=", "owner": "libbpf", "repo": "blazesym", "rev": "6beb39ebc8e3a604c7b483951c85c831c1bbe0d1", "type": "github" }, "original": { "owner": "libbpf", "repo": "blazesym", "rev": "6beb39ebc8e3a604c7b483951c85c831c1bbe0d1", "type": "github" } }, "flake-compat": { "flake": false, "locked": { "lastModified": 1650374568, "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", "owner": "edolstra", "repo": "flake-compat", "rev": "b4a34015c698c7793d592d66adbab377907a2be8", "type": "github" }, "original": { "owner": "edolstra", "repo": "flake-compat", "type": "github" } }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { "lastModified": 1731533236, "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { "owner": "numtide", "repo": "flake-utils", "type": "github" } }, "naersk": { "inputs": { "nixpkgs": [ "nixpkgs" ] }, "locked": { "lastModified": 1739824009, "narHash": "sha256-fcNrCMUWVLMG3gKC5M9CBqVOAnJtyRvGPxptQFl5mVg=", "owner": "nix-community", "repo": "naersk", "rev": "e5130d37369bfa600144c2424270c96f0ef0e11d", "type": "github" }, "original": { "owner": "nix-community", "repo": "naersk", "type": "github" } }, "nix-appimage": { "inputs": { "appimage-runtime": "appimage-runtime", "flake-compat": "flake-compat", "flake-utils": [ "flake-utils" ], "nixpkgs": [ "nixpkgs" ], "squashfuse": "squashfuse" }, "locked": { "lastModified": 1733518359, "narHash": "sha256-cAjuMKREglYFnhNp1sps3t5gTiLxQpsYNko7Nj+lbtA=", "owner": "danobi", "repo": "nix-appimage", "rev": "74e44691812b4f220e84fd89895931ff4f904a03", "type": "github" }, "original": { "owner": "danobi", "repo": "nix-appimage", "rev": "74e44691812b4f220e84fd89895931ff4f904a03", "type": "github" } }, "nixpkgs": { "locked": { "lastModified": 1742738698, "narHash": "sha256-KCtAXWwQs03JmEhP4ss59QVzT+rHZkhQO85KjNy8Crc=", "owner": "NixOS", "repo": "nixpkgs", "rev": "f3a2a0601e9669a6e38af25b46ce6c4563bcb6da", "type": "github" }, "original": { "owner": "NixOS", "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "blazesym": "blazesym", "flake-utils": "flake-utils", "naersk": "naersk", "nix-appimage": "nix-appimage", "nixpkgs": "nixpkgs" } }, "squashfuse": { "flake": false, "locked": { "lastModified": 1655253282, "narHash": "sha256-RIhDXzpmrYUOwj5OYzjWKJw0cwE+L3t/9pIkg/hFXA0=", "owner": "vasi", "repo": "squashfuse", "rev": "d1d7ddafb765098b34239eacaf2f9abee1fbc27c", "type": "github" }, "original": { "owner": "vasi", "repo": "squashfuse", "type": "github" } }, "systems": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "owner": "nix-systems", "repo": "default", "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "type": "github" }, "original": { "owner": "nix-systems", "repo": "default", "type": "github" } } }, "root": "root", "version": 7 } bpftrace-0.23.2/flake.nix000066400000000000000000000273321477746507000152160ustar00rootroot00000000000000{ description = "High-level tracing language for Linux"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-utils.url = "github:numtide/flake-utils"; nix-appimage = { # We're maintaining a fork b/c upstream is missing support for unstable # and has also dropped the following feature we depend on: # https://github.com/ralismark/nix-appimage/pull/9 # # Also b/c appimage-runtime (which nix-appimage depends on) has a bug # that's being fixed in: # https://github.com/AppImageCrafters/appimage-runtime/pull/14 url = "github:danobi/nix-appimage/74e44691812b4f220e84fd89895931ff4f904a03"; # Avoid multiple copies of the same dependency inputs.nixpkgs.follows = "nixpkgs"; inputs.flake-utils.follows = "flake-utils"; }; naersk = { url = "github:nix-community/naersk"; # See above inputs.nixpkgs.follows = "nixpkgs"; }; blazesym = { url = "github:libbpf/blazesym/6beb39ebc8e3a604c7b483951c85c831c1bbe0d1"; flake = false; }; }; outputs = { self, nixpkgs, flake-utils, nix-appimage, naersk, blazesym, ... }: # This flake only supports 64-bit linux systems. # Note bpftrace support aarch32 but for simplicity we'll omit it for now. flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system: let pkgs = import nixpkgs { inherit system; }; # The default LLVM version is the latest supported release defaultLlvmVersion = 20; # Override to specify the libbpf build we want libbpfVersion = "1.5.0"; libbpf = pkgs.libbpf.overrideAttrs { version = libbpfVersion; src = pkgs.fetchFromGitHub { owner = "libbpf"; repo = "libbpf"; rev = "v${libbpfVersion}"; # Nix uses the hash to do lookups in its cache as well as check that the # download from the internet hasn't changed. Therefore, it's necessary to # update the hash every time you update the source. Failure to update the # hash in a cached environment (warm development host and CI) will cause # nix to use the old source and then fail at some point in the future when # the stale cached content is evicted. # # If you don't know the hash, set: # sha256 = ""; # then nix will fail the build with such an error message: # hash mismatch in fixed-output derivation '/nix/store/m1ga09c0z1a6n7rj8ky3s31dpgalsn0n-source': # specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= # got: sha256-173gxk0ymiw94glyjzjizp8bv8g72gwkjhacigd1an09jshdrjb4 sha256 = "sha256-+L/rbp0a3p4PHq1yTJmuMcNj0gT5sqAPeaNRo3Sh6U8="; }; }; # Override to specify the bcc build we want. # First overrides with the above libbpf and then overrides the rev. bccVersion = "0.33.0"; bcc = (pkgs.bcc.override { libbpf = libbpf; llvmPackages = pkgs."llvmPackages_${toString defaultLlvmVersion}"; }).overridePythonAttrs { version = bccVersion; src = pkgs.fetchFromGitHub { owner = "iovisor"; repo = "bcc"; rev = "v${bccVersion}"; # See above sha256 = "sha256-6dT3seLuEVQNKWiYGLK1ajXzW7pb62S/GQ0Lp4JdGjc="; }; }; # Download statically linked vmtest binary arch = pkgs.lib.strings.removeSuffix "-linux" system; vmtestVersion = "0.18.0"; # Architecture-specific SHA values. # You can get the sha by using the trick above and running `nix develop --system aarch64-linux`. # It'll error out on the actual build, but the SHA check is done before that. vmtestSha = { "x86_64" = "sha256:1wv49fq7n820jj7zyvbvrrzg2vwvyy8kb3gfw1lg55rzfqzhl9v3"; "aarch64" = "sha256:1nsq32bn6pd1gmij1qlry8ydn4gp0jdcqs030ba6yh2c30rhi02d"; }; vmtest = pkgs.stdenv.mkDerivation { name = "vmtest"; version = vmtestVersion; src = builtins.fetchurl { url = "https://github.com/danobi/vmtest/releases/download/v${vmtestVersion}/vmtest-${arch}"; sha256 = vmtestSha.${arch}; }; # Remove all other phases b/c we already have a prebuilt binary phases = [ "installPhase" ]; installPhase = '' install -m755 -D $src $out/bin/vmtest ''; }; # Build blazesym blazesym_c = naersk.lib.${system}.buildPackage { root = blazesym; cargoBuildOptions = x: x ++ [ "-p" "blazesym-c" ]; copyLibs = true; postInstall = '' # Export C headers mkdir -p $out/include cp capi/include/*.h $out/include/ ''; }; # Define lambda that returns a derivation for a kernel given kernel version and SHA as input mkKernel = kernelVersion: sha256: with pkgs; stdenv.mkDerivation rec { name = "kernel"; version = kernelVersion; src = builtins.fetchurl { url = "https://github.com/bpftrace/kernels/releases/download/assets/linux-v${kernelVersion}.tar.zst"; sha256 = sha256; }; # Remove all other phases b/c we already have a prebuilt binary phases = [ "installPhase" ]; installPhase = '' mkdir -p $out tar xvf $src --strip-components=1 -C $out ''; nativeBuildInputs = [ gnutar zstd ]; }; # Define lambda that returns a derivation for bpftrace given llvm version as input mkBpftrace = llvmVersion: pkgs.stdenv.mkDerivation { name = "bpftrace"; src = self; nativeBuildInputs = [ pkgs.bison pkgs.clang pkgs.cmake pkgs.flex pkgs.gcc pkgs.ninja ]; buildInputs = [ bcc blazesym_c libbpf pkgs.asciidoctor pkgs.cereal pkgs.elfutils pkgs.gtest pkgs.libbfd pkgs.libelf pkgs.libffi pkgs.libopcodes pkgs.libpcap pkgs.libsystemtap pkgs."llvmPackages_${toString llvmVersion}".libclang pkgs."llvmPackages_${toString llvmVersion}".llvm pkgs.pahole pkgs.xxd pkgs.zlib ]; # Release flags cmakeFlags = [ "-DCMAKE_BUILD_TYPE=Release" ]; # Technically not needed cuz package name matches mainProgram, but # explicit is fine too. meta.mainProgram = "bpftrace"; }; # Define lambda that returns a devShell derivation with extra test-required packages # given the bpftrace package derivation as input mkBpftraceDevShell = pkg: with pkgs; pkgs.mkShell { buildInputs = [ bc binutils bpftools coreutils clang-tools # Needed for the nix-aware "wrapped" clang-tidy gawk git gnugrep go # For runtime tests iproute2 kmod # For git-clang-format libclang.python nftables procps python3 python3Packages.looseversion qemu_kvm rustc # For runtime tests strace unixtools.ping util-linux vmtest ] ++ pkg.nativeBuildInputs ++ pkg.buildInputs; # Some hardening features (like _FORTIFY_SOURCE) requires building with # optimizations on. That's fine for actual flake build, but for most of the # dev builds we do in nix shell, it just causes warning spew. hardeningDisable = [ "all" ]; }; in { # Set formatter for `nix fmt` command formatter = pkgs.nixpkgs-fmt; # Define package set packages = rec { default = self.packages.${system}."bpftrace-llvm${toString defaultLlvmVersion}"; # Support matrix of llvm versions bpftrace-llvm20 = mkBpftrace 20; bpftrace-llvm19 = mkBpftrace 19; bpftrace-llvm18 = mkBpftrace 18; bpftrace-llvm17 = mkBpftrace 17; bpftrace-llvm16 = mkBpftrace 16; # Self-contained static binary with all dependencies appimage = nix-appimage.mkappimage.${system} { drv = default; entrypoint = pkgs.lib.getExe default; name = default.name; # Exclude the following groups to reduce appimage size: # # *.a: Static archives are not necessary at runtime # *.h: Header files are not necessary at runtime (some ARM headers for clang are large) # *.py, *.pyc, *.whl: bpftrace does not use python at runtime # libLLVM-11.so: Appimage uses the latest llvm we support, so not llvm11 # # The basic process to identify large and useless files is to: # # ``` # $ nix build .#appimage # $ ./result --appimage-mount # $ cd /tmp/.mount_resultXXXX # in new terminal # $ fd -S +1m -l # ``` exclude = [ "... *.a" "... *.h" "... *.py" "... *.pyc" "... *.whl" "... libLLVM-11.so" ]; }; # Kernels to run runtime tests against. # # Right now these just mirror the published kernels at # https://github.com/bpftrace/kernels. Over time we'll firm up our # kernel test policy. kernel-5_15 = mkKernel "5.15" "sha256:05awbz25mbiy47zl7xvaf9c37zb6z71sk12flbqli7yppi7ryd13"; kernel-6_1 = mkKernel "6.1" "sha256:1b7bal1l8zy2fkr1dbp0jxsrzjas4yna78psj9bwwbs9qzrcf5m9"; kernel-6_6 = mkKernel "6.6" "sha256:19chnfwv84mc0anyf263vgg2x7sczypx8rangd34nf3sywb5cv5y"; kernel-6_12 = mkKernel "6.12" "sha256:1va2jx3w70gaiqxa8mfl3db7axk2viys8qf65l9qyjy024vn26ib"; }; # Define apps that can be run with `nix run` apps.default = { type = "app"; program = "${self.packages.${system}.default}/bin/bpftrace"; }; devShells = rec { default = self.devShells.${system}."bpftrace-llvm${toString defaultLlvmVersion}"; bpftrace-llvm20 = mkBpftraceDevShell self.packages.${system}.bpftrace-llvm20; bpftrace-llvm19 = mkBpftraceDevShell self.packages.${system}.bpftrace-llvm19; bpftrace-llvm18 = mkBpftraceDevShell self.packages.${system}.bpftrace-llvm18; bpftrace-llvm17 = mkBpftraceDevShell self.packages.${system}.bpftrace-llvm17; bpftrace-llvm16 = mkBpftraceDevShell self.packages.${system}.bpftrace-llvm16; }; }); } bpftrace-0.23.2/images/000077500000000000000000000000001477746507000146525ustar00rootroot00000000000000bpftrace-0.23.2/images/bpftrace.graffle000066400000000000000000005120101477746507000177670ustar00rootroot00000000000000 ApplicationVersion com.omnigroup.OmniGrafflePro 138.29.0.155790 CreationDate 2016-03-04 01:58:06 +0000 Creator Brendan Gregg GraphDocumentVersion 6 GuidesLocked NO GuidesVisible YES ImageCounter 1 LinksVisible NO MagnetsVisible NO MasterSheets ModificationDate 2018-09-06 19:01:57 +0000 Modifier Brendan Gregg NotesVisible NO OriginVisible NO PageBreaks YES PrintInfo NSBottomMargin float 41 NSHorizonalPagination int 0 NSLeftMargin float 18 NSPaperSize coded BAtzdHJlYW10eXBlZIHoA4QBQISEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAx7X05TU2l6ZT1mZn2WgWQCgRgDhg== NSPrintReverseOrientation int 0 NSRightMargin float 18 NSTopMargin float 18 ReadOnly NO Sheets ActiveLayerIndex 0 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {720, 504}} Class SolidGraphic FontInfo Font CourierNewPS-BoldMT Size 16 ID 2 Style shadow Draws NO stroke Draws NO CanvasOrigin {0, 0} CanvasSize {720, 504} ColumnAlign 1 ColumnSpacing 36 DisplayScale 1 0/72 in = 1.0000 in GraphicsList Class LineGraphic ID 218 Points {100.56071, 286.80136} {100.56071, 336.21786} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 217 Points {100.56071, 243.74657} {100.56071, 218.11617} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 212 Points {116.51337, 211.42293} {17.765594, 211.42293} Style stroke HeadArrow 0 TailArrow 0 Bounds {{594.2088, 450.04092}, {79, 16}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue Size 13 ID 210 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs26 \cf0 Timed Events} VerticalPad 0 Wrap NO Bounds {{592.52771, 397.79242}, {98, 40}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 18 ID 209 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs36 \cf0 profile:\ interval:} VerticalPad 0 Wrap NO Bounds {{11.168304, 452.83752}, {86, 16}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue Size 13 ID 208 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs26 \cf0 Special Events} VerticalPad 0 Wrap NO Bounds {{11.542926, 401.00977}, {55, 40}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 18 ID 207 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs36 \cf0 BEGIN\ END} VerticalPad 0 Wrap NO Bounds {{674.00085, 225.2804}, {26, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 14 ID 206 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\b\fs28 \cf0 bus} VerticalPad 0 Wrap NO Class LineGraphic ID 205 Points {707.11572, 245.2608} {667.31061, 245.2608} Style stroke HeadArrow 0 TailArrow 0 Width 1.5 Bounds {{594.2088, 59.240166}, {98, 21}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 18 ID 203 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs36 \cf0 hardware:} VerticalPad 0 Wrap NO Bounds {{127.97746, 401.00977}, {98, 21}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 18 ID 202 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs36 \cf0 software:} VerticalPad 0 Wrap NO Bounds {{137.27701, 60.738724}, {119, 21}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 18 ID 200 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\b\fs36 \cf0 tracepoint:} VerticalPad 0 Wrap NO Class LineGraphic ID 199 Points {257.00269, 358.54868} {268.39191, 330.24179} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{240.63177, 358.47018}, {34, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 14 ID 198 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs28 \cf0 scsi} VerticalPad 0 Wrap NO Class LineGraphic ID 197 Points {132.44882, 339.89978} {132.68912, 273.06277} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{130.54706, 340.3559}, {34, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 14 ID 196 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\b\fs28 \cf0 jbd2} VerticalPad 0 Wrap NO Bounds {{485.17822, 484.28946}, {218, 15}} Class ShapedGraphic FitText YES Flow Resize FontInfo Color w 0.697622 Font HelveticaNeue Size 12 ID 138 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} {\colortbl;\red255\green255\blue255;\red178\green178\blue178;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\fs24 \cf2 https://github.com/bpftrace/bpftrace 2018} VerticalPad 0 Wrap NO Class LineGraphic ID 195 Points {452.44153, 402.19925} {452.26917, 296.48868} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{431.7403, 403.79242}, {101, 48}} Class ShapedGraphic FitText YES Flow Resize FontInfo Color b 0.6 g 0.6 r 0.6 Font CourierNewPS-BoldMT Size 14 ID 194 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs28 \cf0 page-faults\ minor-faults\ major-faults} VerticalPad 0 Wrap NO Class LineGraphic ID 193 Points {390.05359, 401.50204} {389.88123, 250.79147} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{284.14206, 404.28827}, {110, 32}} Class ShapedGraphic FitText YES Flow Resize FontInfo Color b 0.6 g 0.6 r 0.6 Font CourierNewPS-BoldMT Size 14 ID 192 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\b\fs28 \cf0 cpu-clock\ cs migrations} VerticalPad 0 Wrap NO Class LineGraphic ID 191 Points {720.67969, 390.10159} {0, 390.10159} Style stroke HeadArrow 0 TailArrow 0 Class LineGraphic Head ID 44 ID 188 Points {645.28192, 366.33118} {645.28192, 344.8429} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Tail ID 186 Class LineGraphic Head ID 43 ID 187 Points {642.23309, 194.14841} {642.18158, 224.80887} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{615.78192, 366.33118}, {59, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPSMT Size 14 ID 186 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\b\fs28 \cf0 cache-*} VerticalPad 0 Wrap NO Bounds {{592.52771, 110.81422}, {101, 80}} Class ShapedGraphic FitText YES Flow Resize FontInfo Color b 0.6 g 0.6 r 0.6 Font CourierNewPS-BoldMT Size 14 ID 185 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\b\fs28 \cf0 cpu-cycles\ instructions\ branch-*\ frontend-*\ backend-*} VerticalPad 0 Wrap NO Class LineGraphic ID 184 Points {580.85297, 51.678741} {580.85297, 504} Style stroke HeadArrow 0 TailArrow 0 Class LineGraphic ID 183 Points {117.76019, 51.820648} {117.76019, 504} Style stroke HeadArrow 0 TailArrow 0 Class LineGraphic ID 181 Points {459.71552, 89.123932} {412.92816, 215.27336} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 180 Points {348.47974, 339.62717} {366.03448, 296.155} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{11.542926, 61.738724}, {97, 16}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue Size 13 ID 174 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs26 \cf0 Dynamic Tracing} VerticalPad 0 Wrap NO Bounds {{277.97556, 61.738724}, {80, 16}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue Size 13 ID 50 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs26 \cf0 Static Tracing} VerticalPad 0 Wrap NO Bounds {{609.698, 321.87671}, {71.167847, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 44 Shape Rectangle Style fill Color b 0.851391 g 0.851391 r 0.851391 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 DRAM} Bounds {{616.98218, 225.30887}, {50.32843, 40.90387}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 43 Shape Rectangle Style fill Color b 0.851391 g 0.851391 r 0.851391 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 CPU\ 1} Class LineGraphic ID 113 Points {621.03168, 243.74657} {470.22656, 243.74657} Style stroke HeadArrow 0 TailArrow 0 Width 1.5 Bounds {{649.10779, 275.54425}, {48, 31}} Class ShapedGraphic FitText YES Flow Resize ID 70 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Memory\ Bus} VerticalPad 0 Wrap NO Bounds {{495.49713, 202.92044}, {73, 31}} Class ShapedGraphic FitText YES Flow Resize ID 68 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 CPU\ Interconnect} VerticalPad 0 Wrap NO Class LineGraphic ID 137 Points {720.67969, 51.205944} {0, 51.205944} Style stroke HeadArrow 0 TailArrow 0 Width 0.5 Class LineGraphic ID 168 Points {194.18262, 359.07861} {214.97746, 305.54657} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 171 Points {154.87547, 126.52167} {154.11154, 259.46808} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{5.5429268, 142.82158}, {109, 60}} Class ShapedGraphic FitText YES Flow Resize FontInfo Color b 0 g 0 r 0 Font CourierNewPS-BoldMT Size 18 ID 165 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\b\fs36 \cf0 uprobe:\ uretprobe:\ usdt:} VerticalPad 0 Wrap NO Bounds {{5.5429268, 245.28812}, {109, 40}} Class ShapedGraphic FitText YES Flow Resize FontInfo Color b 0 g 0 r 0 Font CourierNewPS-BoldMT Size 18 ID 163 Shape Rectangle Style shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\b\fs36 \cf0 kprobe:\ kretprobe:} VerticalPad 0 Wrap NO Class LineGraphic ID 161 Points {490.20508, 189.45589} {457.79449, 243.1916} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{483.17822, 107.67329}, {76, 80}} Class ShapedGraphic FitText YES Flow Resize FontInfo Color b 0.6 g 0.6 r 0.6 Font CourierNewPS-BoldMT Size 14 ID 160 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs28 \cf0 sched\ task\ signal\ timer\ workqueue} VerticalPad 0 Wrap NO Class LineGraphic ID 159 Points {491.18097, 339.74957} {454.96002, 324.67889} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 158 Points {478.23856, 282.89941} {449.48291, 283.48987} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{484.61728, 257.14508}, {76, 48}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPSMT Size 14 ID 157 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs28 \cf0 kmem\ vmscan\ writeback} VerticalPad 0 Wrap NO Bounds {{495.49713, 335.2966}, {26, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPSMT Size 14 ID 155 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\b\fs28 \cf0 irq} VerticalPad 0 Wrap NO Bounds {{175.27701, 358.91217}, {43, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 14 ID 154 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\b\fs28 \cf0 block} VerticalPad 0 Wrap NO Bounds {{145.91074, 104.60594}, {34, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 14 ID 153 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\b\fs28 \cf0 ext4} VerticalPad 0 Wrap NO Bounds {{432.88959, 69.687424}, {68, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 14 ID 152 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\b\fs28 \cf0 syscalls} VerticalPad 0 Wrap NO Bounds {{387.52762, 104.94469}, {34, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPS-BoldMT Size 14 ID 201 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\b\fs28 \cf0 sock} VerticalPad 0 Wrap NO Class LineGraphic ID 142 Points {403.37744, 124.20242} {360.62976, 234.90355} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{338.05746, 340.3559}, {26, 32}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font CourierNewPSMT Size 14 ID 141 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 CourierNewPS-BoldMT;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\b\fs28 \cf0 net\ skb} VerticalPad 0 Wrap NO Bounds {{247.66663, 21.18483}, {186, 26}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Thin Size 21 ID 136 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Thin;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs42 \cf0 bpftrace Probe Types} VerticalPad 0 Wrap NO Bounds {{127.24895, 311.14508}, {342.55994, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 90 Shape Rectangle Style fill Color b 0.989631 g 0.927333 r 0.755614 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Device Drivers} Bounds {{198.2724, 176.34805}, {271.53644, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 94 Shape Rectangle Style fill Color b 0.816575 g 0.817319 r 0.990984 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 System Libraries} Bounds {{240.47348, 115.41571}, {105, 16}} Class ShapedGraphic FitText YES Flow Resize ID 46 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Operating System} VerticalPad 0 Wrap NO Bounds {{378.9624, 266.21274}, {90.846375, 44.932343}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 89 Shape Rectangle Style fill Color b 0.802726 g 0.939072 r 0.998695 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Virtual Memory} Bounds {{378.9624, 221.2804}, {90.846375, 44.932343}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 85 Shape Rectangle Style fill Color b 0.802726 g 0.939072 r 0.998695 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Scheduler\ } Bounds {{277.97556, 288.67892}, {100.98691, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 88 Shape Rectangle Style fill Color b 0.797729 g 0.916695 r 0.851254 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Ethernet} Bounds {{277.97556, 266.21274}, {100.98691, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 87 Shape Rectangle Style fill Color b 0.797729 g 0.916695 r 0.851254 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 IP} Bounds {{277.97556, 243.74657}, {100.98691, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 86 Shape Rectangle Style fill Color b 0.797729 g 0.916695 r 0.851254 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 TCP/UDP} Bounds {{277.97556, 221.2804}, {100.98691, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 84 Shape Rectangle Style fill Color b 0.797729 g 0.916695 r 0.851254 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Sockets} Bounds {{127.24895, 288.67889}, {150.72662, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 81 Shape Rectangle Style fill Color b 0.989599 g 0.860461 r 0.79432 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Block Device Interface} Bounds {{127.24889, 266.21274}, {150.72662, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 82 Shape Rectangle Style fill Color b 0.989599 g 0.860461 r 0.79432 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Volume Manager} Bounds {{127.24895, 243.74657}, {150.72662, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 80 Shape Rectangle Style fill Color b 0.989599 g 0.860461 r 0.79432 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 File Systems} Bounds {{127.24889, 221.2804}, {150.72662, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 79 Shape Rectangle Style fill Color b 0.989599 g 0.860461 r 0.79432 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 VFS} Bounds {{127.24895, 198.81422}, {342.55994, 22.466171}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 83 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 System Call Interface} Bounds {{127.24895, 137.62154}, {342.55994, 61.192688}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 78 Shape Rectangle Style fill Color b 0.816575 g 0.817319 r 0.990984 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;\f1\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Applications\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f1\b \cf0 \ } Class LineGraphic ID 114 Points {642.23309, 266.71274} {642.52075, 336.21786} Style stroke HeadArrow 0 TailArrow 0 Width 1.5 GridInfo HPages 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 Orientation 1 PrintOnePage RowAlign 1 RowSpacing 36 SheetTitle probe types 2018 UniqueID 35 VPages 1 ActiveLayerIndex 0 AutoAdjust BackgroundGraphic Bounds {{0, 0}, {720, 504}} Class SolidGraphic FontInfo Font HelveticaNeue-Medium Size 14 ID 2 Style shadow Draws NO stroke Draws NO CanvasOrigin {0, 0} CanvasSize {720, 504} ColumnAlign 1 ColumnSpacing 36 DisplayScale 1 0/72 in = 1.0000 in GraphicsList Class LineGraphic ID 273 Points {367.90503, 278.2301} {367.90503, 327.57556} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 272 Points {341.46149, 158.34779} {128.52332, 124.22475} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 271 Points {327.12799, 220.11488} {129.4769, 242.09456} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{469.24796, 151.50769}, {65, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 270 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 process structs} VerticalPad 0 Wrap NO Class LineGraphic Head ID 179 ID 266 Points {402.69992, 177.07678} {402.69992, 192.24722} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Tail ID 264 Bounds {{77.252029, 115.34409}, {49, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 265 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 structs_} VerticalPad 0 Wrap NO Bounds {{341.64667, 142.57678}, {122.10646, 34}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 264 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Clang Parser\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \fs18 \cf0 clang_parser.*} Bounds {{596.92767, 149.64587}, {45, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 13 ID 263 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs26 \cf0 Events:} VerticalPad 0 Wrap NO Class LineGraphic ID 262 Points {327.71173, 261.40762} {130.06064, 283.3873} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{204.50983, 255.98492}, {57.269684, 12}} Class ShapedGraphic FitText Vertical Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 259 Rotation 353 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 name_ids_} VerticalPad 0 Class LineGraphic ID 256 Points {328.19995, 198.0903} {128.40494, 153.61374} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{52.51329, 134.49333}, {73, 36}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 255 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 printf_args_\ stackid_map_\ ...} VerticalPad 0 Wrap NO Bounds {{208.23531, 102.76789}, {73, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 253 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 bpftrace program} VerticalPad 0 Wrap NO Bounds {{367.90503, 455.75885}, {85, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Italic Size 14 ID 252 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 AsyncAction::*} VerticalPad 0 Wrap NO Bounds {{266.16663, 455.78265}, {49, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Italic Size 14 ID 251 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 printf()} VerticalPad 0 Wrap NO Bounds {{483.05966, 288.89569}, {60, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 250 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 BPF func calls} VerticalPad 0 Wrap NO Bounds {{483.05969, 249.63867}, {58, 23}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 249 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 AST Nodes to\ LLVM IR calls} VerticalPad 0 Wrap NO Class LineGraphic ID 248 Points {671.17255, 367.70795} {671.17255, 245.32845} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 247 Points {665.60895, 432.4906} {665.60895, 442.52478} Style stroke HeadArrow 0 HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 246 Points {633.69141, 393.59317} {633.69141, 407.73416} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{609.55481, 328.13684}, {50.13974, 18}} Class ShapedGraphic FontInfo Font HelveticaNeue-Medium Size 12 ID 245 Shape Rectangle Style fill Color b 0.880332 g 0.985746 r 0.904703 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs24 \cf0 Verifier} Bounds {{618.401, 285.35843}, {26, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 13 ID 244 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs26 \cf0 BPF} VerticalPad 0 Wrap NO Bounds {{596.92767, 227.44653}, {79, 18}} Class ShapedGraphic FontInfo Font HelveticaNeue-Medium Size 11 ID 243 Shape Rectangle Style fill Color b 0.880332 g 0.985746 r 0.904703 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs22 \cf0 perf_events} Bounds {{596.92767, 173.44653}, {79, 18}} Class ShapedGraphic FontInfo Font HelveticaNeue-Medium Size 11 ID 242 Shape Rectangle Style fill Color b 0.880332 g 0.985746 r 0.904703 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs22 \cf0 tracepoints} Bounds {{597.04163, 209.44653}, {79, 18}} Class ShapedGraphic FontInfo Font HelveticaNeue-Medium Size 11 ID 241 Shape Rectangle Style fill Color b 0.880332 g 0.985746 r 0.904703 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs22 \cf0 uprobes} Bounds {{597.04163, 191.44653}, {79, 18}} Class ShapedGraphic FontInfo Font HelveticaNeue-Medium Size 11 ID 240 Shape Rectangle Style fill Color b 0.880332 g 0.985746 r 0.904703 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs22 \cf0 kprobes} Bounds {{265.43835, 434.62363}, {166, 14}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 11 ID 239 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs22 \cf0 Per-event Output, Async Actions} VerticalPad 0 Wrap NO Bounds {{52.413818, 412.15796}, {67, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 238 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 print_map()} VerticalPad 0 Wrap NO Bounds {{601.33826, 406.15381}, {63, 18}} Class ShapedGraphic FontInfo Font HelveticaNeue-Medium Size 12 ID 237 Shape Rectangle Style fill Color b 0.880332 g 0.985746 r 0.904703 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs24 \cf0 Maps} Bounds {{54.570164, 438.77698}, {67, 24}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 236 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 perf_event_\ printer()} VerticalPad 0 Wrap NO Bounds {{302.99918, 402.28421}, {92, 14}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 11 ID 235 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs22 \cf0 Async Summaries} VerticalPad 0 Wrap NO Bounds {{599.61194, 442.52478}, {71.560608, 18}} Class ShapedGraphic FontInfo Font HelveticaNeue-Medium Size 12 ID 234 Shape Rectangle Style fill Color b 0.880332 g 0.985746 r 0.904703 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs24 \cf0 perf buffer} Class LineGraphic ID 231 Points {599.61194, 452.52454} {124.47879, 452.52454} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 230 Points {601.33826, 419.89258} {118.98193, 419.89258} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 229 Points {57.801025, 197.90755} {41.32082, 198.16011} {40.85701, 393.81531} {149.95813, 393.81531} Style stroke HeadArrow 0 TailArrow 0 Width 1.7000000476837158 Bounds {{258.29111, 353.49463}, {235, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 221 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 bpf_attach_*(), bcc_usdt_enable_probe()} VerticalPad 0 Wrap NO Bounds {{257.2486, 322.3833}, {91, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 220 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 bpf_load_prog()} VerticalPad 0 Wrap NO Bounds {{185.80527, 301.36035}, {53, 28}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 214 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 bcc\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \fs18 \cf0 libbpf/libbcc} VerticalPad 0 Wrap NO Bounds {{175.31076, 294.23932}, {74, 87.786255}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 213 Line ID 219 Position 0.15744693577289581 RotationType 0 Shape Rectangle Style fill Color b 0.988235 g 0.968627 r 0.843137 shadow Draws NO Bounds {{350.28506, 327.60806}, {104.45963, 21.979675}} Class ShapedGraphic FontInfo Font HelveticaNeue-Medium Size 13 ID 183 Line ID 219 Offset -0.4654541015625 Position 0.56086909770965576 RotationType 0 Shape Rectangle Style fill Color b 0.918349 g 0.918349 r 0.918349 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 BPF bytecode} Class LineGraphic ID 219 Points {138.07822, 338.13245} {609.55487, 338.13245} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic Head ID 212 ID 218 Points {92.362823, 273.69583} {92.611145, 293.6806} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Tail ID 202 Class LineGraphic ID 217 Points {138.15176, 368.62692} {671.17255, 368.62692} Style stroke HeadArrow 0 HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{48.773739, 294.18057}, {88.77803, 87.786255}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 212 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;\f1\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Attached\ Probes \fs28 \ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f1\fs16 \cf0 attached_probes_} Bounds {{183.37308, 191.73579}, {79, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 211 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 create_maps()} VerticalPad 0 Wrap NO Bounds {{165.10924, 215.05168}, {126.24751, 12}} Class ShapedGraphic FitText Vertical Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 210 Rotation 353 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 bpftrace_.add_probe()} VerticalPad 0 Bounds {{57.851265, 234.58633}, {68.530945, 38.609528}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 202 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;\f1\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Probes\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f1\fs18 \cf0 probes_} Bounds {{469.24796, 74.576599}, {74, 23}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 200 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 parse bpftrace\ program into AST} VerticalPad 0 Wrap NO Bounds {{483.05969, 191.73579}, {63, 34}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 199 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs18 \cf0 syntax checks,\ map creation,\ add probes} VerticalPad 0 Wrap NO Bounds {{305.83975, 378.62137}, {97, 12}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 198 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs20 \cf0 bpf_create_map()} VerticalPad 0 Wrap NO Class LineGraphic ID 196 Points {149.95813, 393.77628} {633.72522, 393.77628} Style stroke HeadArrow 0 HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 195 Points {327.44894, 207.44949} {127.90216, 207.71301} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Bounds {{57.851265, 180.0654}, {68.530945, 48}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 194 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;\f1\fmodern\fcharset0 Courier;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Maps\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \fs18 \cf0 maps.*\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f1 \cf0 maps_} Bounds {{52.157158, 79.419266}, {72, 28}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 193 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 bpftrace\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \fs18 \cf0 main.*, bpftrace.*} VerticalPad 0 Wrap NO Bounds {{613.19141, 79.419266}, {39, 17}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Medium Size 14 ID 160 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 0 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural \f0\fs26 \cf0 Kernel} VerticalPad 0 Wrap NO Bounds {{211.40228, 76.429428}, {68.530945, 21.979675}} Class ShapedGraphic FontInfo Font HelveticaNeue-Medium Size 14 ID 189 Line ID 190 Position 0.49987009167671204 RotationType 0 Shape Rectangle Style fill Color b 0.918349 g 0.918349 r 0.918349 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 program} Class LineGraphic Head ID 83 ID 190 Points {150.42352, 87.419266} {340.96149, 87.419266} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic ID 188 Points {430.47937, 312.56189} {430.47937, 327.76917} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Class LineGraphic Head ID 181 ID 187 Points {402.69992, 227.24722} {402.69992, 243.26457} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Tail ID 179 Bounds {{377.73087, 110.03512}, {50.13974, 18}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 180 Shape Rectangle Style fill Color b 0.918349 g 0.918349 r 0.918349 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs24 \cf0 AST} Class LineGraphic Head ID 264 ID 92 Points {402.55963, 104.91927} {402.655, 142.07678} Style stroke HeadArrow FilledArrow HeadScale 0.75 LineType 1 TailArrow 0 Width 1.7000000476837158 Tail ID 83 Bounds {{27.851707, 70.806091}, {122.10646, 405.32471}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 186 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Bounds {{589.73248, 275.51532}, {95, 156.49527}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 185 Shape Rectangle Style fill Color b 0.880332 g 0.985746 r 0.904703 shadow Draws NO Bounds {{580.16034, 70.806076}, {113.48351, 405.32474}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 184 Shape Rectangle Style fill Color b 0.880332 g 0.985746 r 0.904703 shadow Draws NO Bounds {{382.19992, 277.76465}, {95, 34}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 182 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 IR Builder\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \fs18 \cf0 ast/irbuilderbpf.*} Bounds {{328.19989, 243.76459}, {149, 34}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 181 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Code Generation\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \fs18 \cf0 ast/codegen_llvm.*} Bounds {{328.19995, 192.74722}, {149, 34}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 179 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Semantic Analyzer\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \fs18 \cf0 ast/semantic_analyser.*} Bounds {{341.46149, 70.419266}, {122.10646, 34}} Class ShapedGraphic FontInfo Font Helvetica-Bold Size 13 ID 83 Shape Rectangle Style fill Color b 0.840392 g 0.984419 r 0.994962 shadow Draws NO Text Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs26 \cf0 Parser\ \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \fs18 \cf0 driver.*, lexer.l, parser.yy} Bounds {{490.45667, 484.28946}, {218, 15}} Class ShapedGraphic FitText YES Flow Resize FontInfo Color w 0.697622 Font HelveticaNeue Size 12 ID 138 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Align 2 Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} {\colortbl;\red255\green255\blue255;\red178\green178\blue178;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr \f0\fs24 \cf2 https://github.com/bpftrace/bpftrace 2018} VerticalPad 0 Wrap NO Class LineGraphic ID 137 Points {720.67969, 51.205944} {0, 51.205944} Style stroke HeadArrow 0 TailArrow 0 Width 0.5 Bounds {{266.16663, 21.18483}, {149, 26}} Class ShapedGraphic FitText YES Flow Resize FontInfo Font HelveticaNeue-Thin Size 21 ID 136 Shape Rectangle Style fill Draws NO shadow Draws NO stroke Draws NO Text Pad 0 Text {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Thin;} {\colortbl;\red255\green255\blue255;} \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc \f0\fs42 \cf0 bpftrace Internals} VerticalPad 0 Wrap NO GridInfo HPages 1 KeepToScale Layers Lock NO Name Layer 1 Print YES View YES LayoutInfo Animate NO circoMinDist 18 circoSeparation 0.0 layoutEngine dot neatoSeparation 0.0 twopiSeparation 0.0 Orientation 1 PrintOnePage RowAlign 1 RowSpacing 36 SheetTitle internals 2018 UniqueID 54 VPages 1 SmartAlignmentGuidesActive YES SmartDistanceGuidesActive YES UseEntirePage WindowInfo CurrentSheet 1 ExpandedCanvases FitInWindow ListView OutlineWidth 142 RightSidebar ShowRuler Sidebar SidebarWidth 126 VisibleRegion {{-80, 0}, {880.95294, 504.00003}} Zoom 2.1488094329833984 ZoomValues probe types 2018 0.0 1 internals 2018 0.0 1 saveQuickLookFiles YES bpftrace-0.23.2/images/bpftrace_probes_2018.png000066400000000000000000007522231477746507000212050ustar00rootroot00000000000000‰PNG  IHDRÜYóºysRGB®Îé pHYsgŸÒRÕiTXtXML:com.adobe.xmp 5 1 2 ‹O²@IDATxìݼÕÙÇñ´$¸CA‹´P ¸Kâ…Å)œ ¥Xq§Bñ/„"I‘†âV(,XBÐyŸÿy9sggg}çÞÝ{çóIvv|¾³;wö™sžÓ+²â( € € € € € P¯À„^½zõ™¤Þ¥Y@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:¸wX0„ € € € € €@ÝÜë¦cA@@@@@:&ëd@zºÀ7ß|ãžzê)7÷ÜsûÝÁãã?vwÞy§{ã7ÜO?ýäæŸ~·Ç{t‡Cë–Çðå—_ºçŸ¾æc›zê©]ß¾}ÝÌ3ÏìzõêUóò]µ@wüÎ¥-_ýu7vìØô覽_d‘EÜL3ÍÔ´õ±"@@èYid,‹ €ÝG`ôèÑná…vÇwœ¼)¦˜Â­³Î:nß}÷u믿~Ëß»Ûw.ëÄéוW^™5©)ㆠæ6ß|󦬋• € € ЀÀ«üÓ‡î ²( €´®Àí·ßî=ôP7pà@wÐA¹•W^Ù)ûÝwßìôÅ_ì&tR÷ûßÿ¾`ûÌ_§û÷ïïæw^·óÎ;ûÏe¹ýb € €@c½"+­‚¥@@ »(@½ð »ãŽ;Î <ØÖwÞéöÝw_÷î»ïú€ëÒK/í–Yfß!©‚Ì#GŽt/¾ø¢Ÿvíµ×ºM6Ù¤$GÖúxàÈ~õÕWÝꫯî–Xb hWZÞûï¿ßo{©¥–r·ß~»›{î¹3ׯ`g²hyÒñ‹_$G;÷ÌEzõê妚jª0Ê¿®³Î:îïÿ»>ôÐCÝYgå÷c®¹æòã´ýË_þâþñ¸Ÿ~úÉS{̘1~Xÿ)hzá…ºSO=ÕÂ_|q·Øb‹ù`¸öW.ôï±Çs_~ù¥?þË/¿Ü-°Àñ:Ê hû‡~¸?& e¤€ï¸qãœñ ,¾ôÒK~Üa‡æÏãä“O^n•N·†G}´;í´Óü|z  Àä’K.é¶<þøãþU<ð@wæ™gx–]yõyÒØÍ7ßÜ 6¬Š%*Ï2à 3¸o¾ùÆ?øÐg¢Þó¶tî¹çúuè¼k_e#÷¯¾úÊŸÏ{ï½×? Xyå•ÝÍ7ßì?ÏaÙôkÖw¢™ß¹ôöºúü¦÷'ý~—]vñß8Þh£Ò“Ý"‹,âƒÉzpUêZP´PjÄž{î鮸⠧keƒf]—R›öoó8z€ ‡jÅ¡yz Ï¤>û ¼ëÁ ®3z §BºžÕó€/ëx‡ €  L°ß–}ô£Š‚ €x zëA|dwÿÞ‚®‘忎¬&sdAõÈŒ™Rÿú׿" òDvsüñ%çK¯ÿšk®ñÛÛ`ƒ "«¹n ZGh,pYMåHÛª¦Xmøh³Í6«8«=`ˆ,`Zv¾?þñ~?í¡ƒ?6 Hû÷Vk4:ýôÓ£‡z(²Úª‘Õ”-XÏ1Çãç³‘æ ¦%ßX-ÔÈjÆGö ² |d ’“‹†-8mµÕV~Ýö` ²àcѰµß~ûE–ª ¢‘‚V;Ý/c5Ÿ3çO®ÿÁŒ¬¶u¤Û÷ߟ9r¤þ ê+n5Ï““2‡ó ¸[-RŒ–^&R`µTÑñéaŲË.Mœ8±ÔlãÏ9ç¿îAƒŒO¾QÀÐjùúùþð‡?Te§å>ø`¿Œˆdýuþäféo²f)wÁøsh5+>$(Z¸ÄˆfÜ­¥†?îvÚ)Þb2à^íù”»‚öº}>è ƒJ5ãØÀ¨Q£"K»ä¬rrR<œüNäñ j•óö§Ôk¥€»¥Ьuˆ—ûþ•Z¿¥Åòçð¤“NŠgIžƒ<¯Kyœ=|Óƒ:k9YZ£ø˜Ê Xj«ÈRLE‹.ºhÓ¾·å¶Ç4@@  pïA'›CE@ *xRpÖÒDGqDUË…™ SmqÒ³j]‡õ«æ·‚Æ{ï½w\Ë4¬£Ü«¥_‰æ™gžÈrW 4çp·ô >Xg©\Êí¦Ÿfi|-RËõ]qÞä :6Ká’U0l9éý>Xšš‚ñ•Þ¨Ö«j+`lyò‹fµñK†‹øyÄe—]æ×yÊ)§”š¥¦ñÍ ¸[J¢¨OŸ>~ÿî»ï¾x?BÀ½–ó©šç²³TñzªÐgV$,Q¤Æé¾y}çÂöZåü†ý)õZ)à®åô IçBç¯Ö¢Öz–lÎAÞ×¥<Î¥òúÖRô9–¡–§ € €4M€€{Ó(Y €ÝD žTƒÔ:ÈŒT#³Ö¢Úç äX' E‹†õ«f¥åL/JÁR´@Æ˽ìׯ”åJw¥¡È:®ô~(°ªZ§[l±EzRÅ÷[o½µ·ÉJ ¡T.J‡¡48–‹¾âºÒ3<ýôÓÞNÁãdÑyÖz-‡~rtUÃÚO}^Ò)uªZAj¦fÜ­ÓÈH=ôYÜÿý ¶îÕžOùXg«¾@©´JH½ÑgUûaùîSS¢(|'òúÎiƒ­t~‹R#ª ¸[¿Þs­µÖJ-]þ­~(Ø®VÉÎAž×¥¼ÎAh¹¢c«¥¨Å>“'žxb-‹1/ € €@ypŸÄn´( € €@À×_í,ã;Ü+˜PÅËî;‘´<ÈÎj¹g.aùÎÝŽ;îèfœqÆÌéåFj9Ëåî¬Fµï”´Ü¼Ížfeg9Û+®ö™gžñ™®´ÒJçMÏ N^ÕÁ§¥ËHOr–Ãw®z 'øa‹f¨0BÞª#Å{î¹§`γÏ>Ûwø©Pk-ê„TÂZëwÆgÔºxÓçÿä“OÜÅ_ìt¬O>ù¤ï„·Ô9«ö|jyuFiéx|»µî´¥³ñåÚƒ|žß¹ît~egq·Æk8Kÿâ;PÍòÌ7dÈßÁ±=4Ëšìò¼.åuìÁ¢?–R×Ú̵‘ê[EÛC¸R³0@@ NÉê\ŽÅ@@ › XîöºŽÐ:Nu‡v˜Sñá‡v–"%s=–=s|¥‘–{Øí»ï¾>øiºõÖ[¯Ò"M›n°º™fš©âú¬ƒWg;ºM6Ù¤â¼é.UôÃ:UuV ¾Ô,Çk¿,ÍM<ŸÕØvçwž³46ÎòÍÇãkÐC³u[Ëbeç}î¹çœÕL/;O˜¨c3fŒ³T9Î:Iõ£­Fº8¢Ï™bd•jΧU`qÖѤÜ[‹…¬ÕTg­œ¥q»í¶›»á†Ü^{핹Lß¹V=¿™5Œ”•>o—^z©³ô(U-©ïÏ,³ÌRö{™Çu)ÏsðË_þÒ»¥†q«­¶ZUšIÊžþùºÜU½fD@z¨÷zâ9l@Ê ((UM`¹Ô:,•‡Ÿ” ì&çÕº8®·l´ÑF>à>zôèN ¸¯³Î:Uí²jê_­EÁâx s1ëÑ׎Þe—]êªeVš¿÷Þ{¾ÖüòË/f©ëUçÓÒTÔµlÖBúìX§¬Y“2Ç)Àn)bÜ*«¬âƒãz(S©E5çÓòÀûÚÿ믿~Cî–ÆÄï·¥/ÉÜÿ¼¾s­z~3j©‡<–ß]}õÕ¾µK©‡*a•<òˆ(sÈ!‡¸P+ŸFIÇ»òÊ+;õ%¡JûDA@È_€€{þÆl@¶˜nºéÞg¥øPíÓ¬¢`|£¥ÿþ%;emtÝ¥–W¸–¢Ž ¯ºê*§ÜÑï¾û®_T5Rçž{n_#U_Õ,W v©¥–r pJ£¢\øÉòÎ;ïø·:îf–ðPD¹°ÓÛ¬u;JÕñÕW_ÕºX—Í_Íù ÁÒf¤ßÐ:n¹åß¡®òº'K^ß¹î|~õ@F)U†êó‘«•EVQë}.Ku––ÉëºÔç@ /¼ðB§Üö÷Þ{¯Sÿ#GŽŒÞ黽È"‹ø<ïêC@×  € €@>…wúùlƒµ"€ €@› „àp#»­Ü×¥jí~óÍ7¬Ú/;~üø¦Ô¤nxGJ¬@cî¸ãŽ®wïÞnË-·ôAçœsÎK”­”$*ü±kFð7lM©9T^z饦Ôâëí.¯zp¤¢”5}ûömè°”úF\ÒÁv­4¯ï\w?¿ÊÕ¯€û%—\â;·Í:AJ'Ó§O·í¶ÛfMŽÇåu]êÌs  ºþ|ðÁîûï¿wO>ù¤{ì±ÇÜ£>ê_/¾øb_ ~­µÖò%/ºè¢ññ3€ € Ðr¸7Ç‘µ € ЭBmêzJ)>ýôÓ’q¥Ri¤üøã>@r¹7²®<–ýÛßþæƒíJé @ªj¹+·r½ÁvícÈñR·4k¿ÃC‘P ·Yëí.ë ¹Û_ýõ†IédÂúÒ+Ëë;×ÝÏïŠ+®è–]vYtÏÊ[®tM>ø Ûj«­|ª´{ò}^×¥®:“O>¹ûõ¯íþøÇ?ú>"”ÊJõTçÌê3¢ÑÏ]Òa@@ÿ àÎ'@Фj¤„\àÊGžU liý?üðC„ÎÚFWûé§Ÿ|煪ͮ\ʵ¦ Ñqe•ðp¡Ù÷È©S²¶Ý“Ç…œù¥r¯WkóÝwßùàf©€{^ß¹žp~•FE)c®»îº¢Ó1dȧ>*¥“Ñ‚y]—Zå(­Ì6Ûlã^~ùe_»]Ç»Ùf›™1@@ 1îù±4 €ÝRà‹/¾pÔèU •_ýêW™> ˜+Vo µ±CºÞõä±ÜóÏ?ï”:Dé+”>¤Öjœ¦}±6¾ó×0®;¼†ÎRÕ)e#å•W^ñ6!€Ÿ^W^ß¹žp~õ]›i¦™|Z™´«úOù*«¬’žTô>¯ëR^çà…^p§œrŠ«õš 7ûï¿¿Ûu×]ݳÏ>ëÓTa0@@ nîuÓ±  €Ý[àüóϯûէʆn˜¹¤‡ž9­š‘ ¢©,¾øâÕÌÞ©ó¨£B•ßüæ75oWixžzê©Ìå” ~ÕUWu·Þz«Sp¶Þ¢”Ê:ˆìׯŸ›a†ÜwÞé[ Ô³^¥:ꨣ| îI&é^·—Êý­”%êì´‘Ðçž{®§-÷¹Èã;×Îï/~ñ _ƒ]ÁãQ£FÅáGyÄ£wß}÷x\¹¼®Kyåœ?æ˜c|š˜rÇUjZ¸>?ñÄ¥fa< € €@ÝëQ,‚ €ÙêhðË/¿ÌžXf¬j]þûßÿöÁðP+;kvurXOQê åHWnâ•VZ©žU-£40Í*ê,VeŽ9æ¨y•—]v™›8qbÉåŽ>úhlWmòzŠÒ¢¼úê«nàÀñâ +dzj¸Ëµž¢‡Jé¡tݱœtÒIþ¼üéOªëðd{íµ×:uT¹úê«—\Gß¹žr~ÕºBÇš¼®ÈSÔî¼óÎ%ÍÓ’˧§•{_×9Xj©¥ÜSLá~øár»VrZèøÍ7ß,9@@j à^»K € ÐíÔÁàøñã ‚WÕôÙgŸíg=þøãK.2`À_£:§KΘ1áÌ3Ïô5±O8ᄌ©µRíXulÚ¬²Øb‹ùUéÁC-E>^tÑEe;V]wÝuÝrË-çþò—¿øóSËúõPá€pÊã¼å–[,ªñJÉ¡ôê¶–¢Úí§Ÿ~º›rÊ)»m>è 6ØÀ·.Бz>³ Ø+7ÿi§V’6Ïï\O8¿óÌ3ÛxãÝ7ÞèÆçô¹¼é¦›ÜFmäfŸ}ö’îÉ y^—ò8 ¶/½ôÒîÑGuõô1pÿý÷ûÃïÛ·o’a@@¸7Èâ €tGEYÄíµ×^>MÈ•W^Yõ!wÜqNµJ•6#ÔM®dÓM7õµÓõúÑG%'•VÀóâ‹/öË*øÜŒ¢`œR´Œ;¶«sK,±„_ÏwÜQõú>ûì3·öÚk;üÖYg¿\©ÎSUËý“O>ñµÉkipòÉ'ûN\•‚b™e–)Ø·i¦™Æzè¡>h·ï¾ûÖ”Zf=öp/½ô’SÊ”Z;ˆ-؉£Úíß~û­Óçî­·Þªjo•‡ÿØcuJôÛßþÖ-¿üò%—Ëó;×Sί>»J³"ïaÆùVÕt–NJž×¥¼Îlês¹Å[ÔôNËÜ~ûí¾Ÿ‰5ÖX#ðŠ € Ð û!@A@¼€¥QO¦‘Σï¾û.²À¹oÁÆŠBVãÜÏÛ¿ÿÈjgΟ\¿å ެVt4ÿüóGÖAkæüa¤ÕºŽ,í‰_¿æ·o˜TòÕjF›m¶YÉéa‚ðýz­†qUô¶m5ዦ¥GXMòÈ‚²~C‡MO.z/+«Y-ÓÈR;Dx _öÉ'Ÿ,š7Œ¸ð #K•-¼ð‘Õl £3_-(|ðÁ‘¥µˆ,gs$ˬb5‚#{Hâ·½æškF–O>k¶xœ=$ˆì¡ŒŸß‚îñøFFŒá×¹ùæ›7ºª’Ë×r>“+±‡O‘µˆˆ¬ÓÙÈj'' [à7Úzë­ý±èó`IŠæÑˆäw"ï\Øh«œß°?¥^-ý‹7»ë®»JÍRr¼¾{¿üå/ý÷BŸaKë}ÿý÷%ç’ç ÏëR^çÀ¢ÅŸ3]C*•?ü0²”\‘µv‰,=U¥Ù™Ž € P½Àx¯¯~~æD@î. <éXTÝ~ûíã`Ž¥hˆ¬Q`Kåã?ެ³Íh½õÖóóX'¦eÀéõ[§†ÑÌ3ÏY:“èÈ#Œžyæ™ÈR«øu+0lTFVc>Zyå•ýú­jdúé•þ«6à®c Ǩ`ÕôŽô€Áj¨Ç›¨5@kµö£9çœÓï³¥´ˆ,uƒ?–à¦`¶{ ®Ï8㌑Ü,´ßžåC÷Ë-ºè¢ÑàÁƒ#«ÕïGràðËZfø¾âŠ+¢çž{.²šñ‘ÕØdk©g| _µwܱ*;KM:餑¥èðÖYh4è¸h>ãŒ3ü¶Äßu×]#Ë;ŸÜµ††[9à®Óƒ=TÒâ½÷ÞÛ;롆Šìß~ûíÈ:?õQänÉ–|È¡eÒ߉fç´déêó›Ü—¬áFîZŸµ´ðß=84hPÖ&ŠÆ¥ÏAž×%m¼Ùç@×Ê=÷ÜÓ?TÓçR׫‡zÈ_s´=]wtÍÑuh¿ýö‹fuVÿÀnÈ!šLA@hž÷æY²&@º‡@:ðŽêž{îñAaÈš|òÉ£©¦š*lÍ5×\щ'žX1ðšµþ·Þz+²T‘ÇZ·þM=õÔ> Þ÷ë×/²Üíq ?ìW¹×jîZ‡¥WˆÎ:ë,_£_Ë)l)@âÕ×pׂz¡‡–J">.Ãô/ׂ .YÞöH5_CQðL­4MÆÛm·]˜Tôª–z‘\§†ÃyÒvô°bÔ¨QEË–ñÏþ3²´3ñ~j=Éó­÷ªµýì³Ï–[M]ÓZ=ஃRmu†ûôéé3«áÜÊÏRvT4ÈúNh¡f}ç²v +ÏoÖþ$Ç5p×¹Þ½{ûóP©õGØnÖ9È뺶™Ç9°~#¢M6Ù$þ 골ϨZÄϥ®³ Îë@@ é>àÞK«µ0  € PQ@{ª3P«Iíóž[PØéŸÕÆvl¬¸üèÑ£¥AqÊõnµ· æWîåÇÜYMo÷Þ{ïùN8g›m6§NH•sÜ‚Èó·ËåYñÅÝ;ï¼ãÿY@ßÍ;ï¼ÎRã8«Ùî,¸ßð¡(ý¿þõ/g-|.z«ÁêÝt^¬}Ýëã7œ朵jðù¡µß:ß ,°@Cë­{‡ZlA}f~øaß鮌,èî;½]vÙe½S3v·Ñï\¹}èŽçWçÁ:{ÐäìáM¹Ã§uåu)s`µÙݘ1cü?{pàì„ï8ÖZÝøþ/t ¢ € €ä"0Á~·ö!àž‹-+E@,r­¬ù‡Ô"pöÙg;k‘â;NµÚòU-Êu©*&fB@¨,àîW©ª¼!æ@@@r°>Ü 3Ìà¬àÜ·Å@@È àž¥Â8@@h+Ç{̧¼²Îl}•¶Úyv@@ Ûpï6§’A@@ ç ¨_å'ßÿý{.GŽ € ÐåÜ»ü° € € P¯ÀG}ävØa7|øpwÀ¸9昣ÞU± € € LÖðX € € Љ×\s{å•WÜûï¿ïîºë.7nÜ8·Â +¸SO=µ÷‚M!€ € P,@À½Ø„1 € € ÐÂ'Ÿ|²{ûí·ÝÔSOíæ›o>wÊ)§¸vÚÉM>ùä-¼×ì € €@OèYé Ê1"€ €@× è¶cüøñ>ϲr-S@z¾ûî;©gÙô2\—Ò"¼G@¨S`B¯^½úp¯SÅ@@@@@øYÀÜé4•Ï € € € € €M àÞDV € € € € €w> € € € € €4A€€{Y € € € € €Üù € € € € € ÐîM@d € € € € €pç3€ € € € € €@¸7‘U € € € € € 0Ù×_ížþy$@@@@@@ Ÿ~úI•ÛWšlĈî˜cŽqQÕ±AVÐ÷¹W¯^­¸kì € € €¹ XÀË}óÍ7®wïÞü.ÎM™#€@–€]¦°ñõ²À‘ö,!Æ!Ц'NtK,±„[qÅݵ×^Û¦GÁn#€ € € P»À)§œâ+–^vÙenÏ=÷¬},Ô/0Á*Àö™¬þåYZQàÍ7ßtcÆŒq_|ñE+îû„ € € €@nï¿ÿ¾_÷ÿþ÷¿Ü¶ÁŠ@rtšZN‡i € € € € €T)@À½J(fC@@@@@ œ÷r:LC@@@@@ JîUB1 € € € € €å¸—Óa € € € € €U p¯ŠÙ@@@@@('@À½œÓ@@@@@¨R€€{•P̆ € € € € €@9îåt˜† € € € € €@•Ü«„b6@@@@@Ê p/§Ã4@@@@@ª à^%³!€ € € € € PN€€{9¦!€ € € € € P¥÷*¡˜ @@@@@rÜËé0 @@@@@*¸W Ål € € € € €” à^N‡i € € € € €T)@À½J(fC@@@@@ œ÷r:LC@@@@@ JîUB1 € € € € €å¸—Óa € € € € €U p¯ŠÙ@@@@@('0Y¹‰LC@@@@ln¸Á >ܽõÖ[3qÄnýõ×/Ç›b1cÆ8>úè£nâĉñ sÌ1‡ûÛßþ¿gv àÞNg‹}E@@@h |Ðm·Ýv™û²óÎ;gŽgd¡À®»îêƒí…c›o¾ùÒ£x@ÛR¦mN;Š € € € Ð*|ðA«ìJÛîÇûï¿ß¶ûÎŽ#PJ Ój¸ßu×]n„ EûÑ«W/×·o_·À ¸Ygµh:#²Æïî¾ûn?QM”¦vÚì›0öïÿ»ûþûïëZÓ¢‹.ê_|ñº–Ís¡'žx½ýöÛn®¹ær+­´Rž›bÝ ÐÛo¿½KÞ,o²É&îCiƒ=góø÷¿ÿíî¼óN÷æ›oº÷Þ{ÏÍ<óÌnÞyçuË.»¬Ûf›mÜd“uÚ-TՇȽAÕT™3roÉÂH@(#°ÕV[¹‘#Gú”2Š1Pj8ï¼óÜ9çœãk¹ûí·µ¯€%hANûµ¸Ï>ûø¬å ¦›n:·È"‹¸£Ž:Ê)ØA)-ðÑGùüšãÅ_t‹-¶X陜²Ë.»¸¯¾úª®µ}ôÑîä“O®kÙ<ºð Ý!CÜ–[nIÀ=Oè:Ö­›”{î¹Çm¾ùæN9Û(t†€‚«Éœ‹ -´Pgl¶îmð=©›®â‚Ï>û¬;à€Ü¿þõ¯’ó{ì±î´ÓN‹ÿ§gÔßeýXèׯ_Ó+”[7÷é3QÛ{î jóbn@pnŠ)¦p—^z©§¸úê«Ýn»íKo¼±Ó?Ý?«² vè´€{€šfšiÜÜsÏÞú×O>ùÄ;Ö}ñÅNAºõÖ[Ï{î¹®ÕƒÒMߨ–ú×_]ttúaÿñÇûñ /¼°›tÒI‹æ¡ÕB #Êès¦ïüwß}çN8á]P+ tð=é°hö:lZwÝuýß6Õ`ßh£ÜŠ+®èfši&§¦®O>ù¤o]¦‡3j¡ŠYa©Ò€Ѝ¶ÎþûïßÔÝÌsݵì(÷µh1/ €ô~»6~–1lÜ5´†@§Ü×Zk-§%éòé§Ÿº—^zÉÙ‡ æî»ï>·êª«ºW_}ÕÍ0à éÙ{ü{™œrÊ)Þ!ï ¶‚dDì'©7égœ1k¶–§ší ìêA¥uFŒáƒíÚ#=Ìyæ™gÜ2Ë,Ó:;Èž Ð|Oò; j¬kìêkÉ%—,ÚØsÏ=çÖYg?Ÿ‚î£Gv³Ì2KÑ|]1‚{ƒÆÔ¹7hÌ¥@@ ¼ÀĉýïÝf¦Ž¢È}öÙg>Ý¡*ƒtFùòË/]Ÿ>}2+]V»}ÅCj—{»:ë\iß;ë³Qíùï óuzÀ½ª~Ü*À®]t‘oέÔ‡v˜»âŠ+J-ÖcÇO?ýô>õNhðÀUC´E "æ°¸r%'‹j‘pOŠ0œ—À­·ÞêtÊl³Í[î•ïI>§Dv¥“QQ:´¬`»¦ 0À]wÝu¾&ü¸q㜀(PÛ …{ƒÆÎ÷ù±4 € <òÈ#îüóÏw?þ¸O•¢à§úZzé¥ÝÊ+¯ìc_Õö ¤eµ>¥>TÅHUPûðÃÝ?üà7¬ jÿþýÝšk®écisÎ9gñ¥Æ¨§RºŽ5ª¨ï>UFQ‹OÝ'ë8{ì1_ádÊ)§t¿úÕ¯|EÐUVY%µÖâ·Zÿßþö7¿U`Q† íURê?kžyæ)^ðç1ŠþãÿðÛOæy×òjq*Ϭòù矻{ï½×çÙOö+©ÌªDó›ßü¦h±vvÖçA­lu¾”ŠòÝwßõÇ÷‹_üÂÿŽÑýîN;íT”y¤¡Ìõ¥¾®TAz‹-¶(ù»©Ì*zÆ$û uJ±Î)#6Ûl³ª¶gµÆüüö´+²‹IUËÔ:“uYY‘ýXŽì‡vU‹Û4²md_ØÈ.J‘]4" ÒTµl˜éÿû_4|øðȾaTÁ«åKxàÈ.¤%ç)X Âû‚EvaòÇZíqVX¥Ÿ|üñÇûs¤ójO'«YÄÏcŠÈjÄG¯¼òJÑ2úæq¬õ|Nt`ú\أ詧žŠì`ѱæ5ÂZŠøób5.óÚDnëµ? ñgJŸ«[n¹%·m±bÚU€ïI>gÎZßÅ×Ý”+?ýôS¤k¬®Söã hVû±à§Yj¼¢ié–£Òÿ­¸ýöÛ£‡z(²T4éY Þײî‚Soòø{©MÔ{o ¿]º·’mºÔj”\^‹u¯§û®×^{-²”eÉÉu·Û½AÝÊ‚ €´¡Àþð/¦û’Î,–Ã=¾ŸÔ}¢Õà.x¯qék¯½vd5Ô+î¦Uº‰æ›o¾¢åÓë ï- zê©×»ÄK”\§–W¬gçwŽ› ëN¿ZEÙ’Ûѽ˜¦[ â’Ëk}öÐ!Úu×] Ìt¼¡XåÜ’ËO2É$‘|²Ê6ÛlSr9m×Zµ,֮κÇ=óÌ3#Kã]öxuÌÖï@¤ïˆbµ ¶G3ÏZ³ôh¥•Vòš°n]8ì©bd9î“«ö“0¤‹.jÚ'«¥Y°=-cOÿüûô2µ¾¯æGµŽOÛ´4AÞG‰Þ½{ûqVƒ0Þd=¾aázŽU0´_V+1¬Æ¿6ú9ÑJî¸ãŽè׿þu¤?pá…ÏÈ?þ-¸à‚~¼‚;yîõ«ÚSö†Ž(Pd}RDö½þ(³¤nv,w™9ꛤãÎkŸ­öA.ëÖ÷Þj9ÔwÀ]¸”®úŒ4R¸7¢WzÙ‡~8¾f[‡¨¥güyÊÈ‘##kÿ=ÕÍz¸æg½Z­¦‚u*À®¿YóZ x½Z¨ÚuëBX_«ÞXÍ&¿Ö‚À{Xí¬ÈZ Äû¼—ªÅ¨×Þ¨²Æ²Ë.M>ùäñºe£%–’/óZÚï Ò>¼G@ ; „ØQWÜÃ}Y¥WÅT1³\QEŽJëÉšn-3K®V¿-§šjª’ëUìHñ¬¬õ¦Ç)—.Ö?R´È"‹Tµ|z}zîªÀh©Ë®Çj\§7ïß÷íÛ·ìrgœqFÁríè¬ßÅÖ¿RÙãÌòÕ¹©5pùå—mGŸ‘άdZpÂZó¸·LJ;ùÅž²9;ùîå—_vV#ÉOûýïïÓͨùÂm·Ýæ;,+Xèç77ÜpƒZmµÕœ}A fQ³uÈúŸÿüÇ)÷¹š¨ŽÖi5 |çhJc‘Ì©¥êÔDÅj*ûu©j¥¹Ð«–UN$5­Ñ:,¨^°½ðÆž4ù&=ZF°i«áímÓM7õ½Z«)†:µ‹­S:MW³™›o¾Ùçä²~Uù­þûßÿúfaíꛎ¨38­_Íi¬½US£Î*;ì°ƒ»é¦›Š6׈o^ÇZÏçDvá…ú&axuö°Ä-¾øâ>—š=@Š?#šFéxýõ×}Ó05yR?ÉrÁø¦JÉqaXi¨ôùµNÍÙÒ%ï¦qv]ozs¾ô1X@Ïÿ墺¨èúdï|Óú^Y@É·t ©¦_‚F›™éš¬f‡ú\'?Ï:°ò}p\|ñž ›:Ž´ ³ïm^©8tíT'’öT<}¸eßkwß}·»êª«üõûƒ>ð¹èt}S“L]G÷Øc¢k~©•ZY ºî‡&˜éyÏ9眪ûÑçA©Et¶›åxUÛn»­³ ªoj©iÊ÷ýÍ7ßø|…º>¨£=TöOâ…2òúždl*¥¿i=©© šÄêo´:mþóŸÿ쬢€ÛqÇcô@VóÓô<¥Þ_ýõNß]}nT´-}wuÝÒ}î-Ôy«>ŸºjFÉëïe#û¦{1õí£ÏZº4bô—¿üÅßs…ï¶®—ʳ¯û"{ÀïSéúz×]wù¿Ñém—zϽA)Æ#€ €@%Ýg*þ¤4/úM {¾Pt¯úé§û> øô«~÷¦‹~ª#{Åô›Xñ¥šI¦NÙ{ï½ÕòÎÌ·®”.ê›O)H’¿aÂv;J—©§žÚ§€Qj«!ON¦yÑHý~Ó½´âZɢ߮º¿]`œbTšn.’³ [íxwÒI'ù}Õ1¦‹U\un¸¡¿·ÖvCQªž#<Ò|ðÁ™ÛÐý|ú~¿Ýœõ{|»í¶+ø<…ã×oeÅå«ßú“,²?ôÐCÑ“£ËÏ>ûìEÓuŸ­sDI tÖÀZk¸k¿ìǦr¢'5¡¬°Â ~œ}™Â¨‚WÕz´¼U~žk¯½6žj.[Þ"_ë[é`BQ3ŠdÁtͶcŽ9ƯOM3,àSBÆ'‘¹üt»ð<Õ 5ÜCÓÕ8×ü¡¨Ö·Žøßb‹-©f(ªåj[i>»‡Iek¸Û¿N» G×\sMÜ|ZOœ¶|¡†µœêiBv¢–¯cÐçàì³ÏެSÜ8O½¾Úz5¸–ªá^Ïç$´¬ÐqZð/RêœPôÔPé”’ç›î‘o©RéIuÒ,k8ýD:˜çÙ4.¯ffaßõÙÙsÏ=ãV>YÇ®1jI‘¬Ö^›ÑÌL× m'¹ý0¬:j)Þ—zU S{0v«â«Ò{„4¥Ö©ñr°|t‘jì—+vS…Ö7åÖ§”Õ{œyÜLŒìánæ´°mÕôµN%7eÁÈŠ5:ºJ½–úž”Ú¨¶Ù› ªfÒÐ~Dx`¤3•R²és¥{ýëÿsk¶³Î:+§é*ªIZ@ÙÍqdð‚Ó ´2¡%˜ýðòÓª]w¹îõþ½,ع*ÞTº75ÜíC®ÓúÞªöÕÓO?í·PQØ5¥r çPM•Ž&{P…{Íc?ÜÂ$ÿÚï ”7 €tsVªá®Ø‚Uä)ˆ#‰ÿÆo,ø½§ßÅ•Šî•”æE¿¥,vf*>Åúõëßéžç…^¨´êH¿}Ò)qÂ=•^•}A1- xûu©õµî«”UA÷réÎÚÏäòú­¶Ï>ûDVù´`_ô^ãÃoÚä2¡†{X@¿g“¿­£K¦7Öoè°¼Ö§{c¥.¶ ñ4Ísì±Ç†Õ½¶“sòÞ6»>ÿº'–W(ŠE*¨sæ ¯Ji]m±‘U‹Ï—U„$p1^k§”Ñþî¾ûîþƒ ¦:©*JË¢…ò;e¥9XLÖ—*”pײ¥>L!³ù曇Åü«‚HZŽ*Þ(Gwø &›p‡€»¦m½õÖaö‚×ðAù¬².‚ú¡Ö­}(¥~T«‰{˜_?j³J20¤æðõ–J?ªµÞ`ª}²Ú¯™¹ÉêõmäX+ý¨ÖþÖú9Yc5¼½Ž'|^“¶ú´üòËÇ燀{äóø‡Ïk½¯VK4É쇬ɳi\ÍÌÂA(½BµÍö²ÌÔ÷CViV33=ÌÚn=ãô .}c–ÞwåƒÓÍ]-ë·ïeØz ¡’•ÖYKÀ}ðàÁ×Wn{Êïn^ÓVó ¡uk»Yß“ôv’ï{rSAk‘™ŽMŽÖ)StÀø|à!€žt Ãá‘æ§‹r‰k]Ju¢YEiï4Oú¡°æ-·îv¸7wŸ¾ÛÉ{›`ш‘riÝz•¬äÖ­óf-mü< ø'Kw¾7H'à €twV ¸+ˆ^ê÷™ÎÕ°ö÷$ºwÑ¿d¥½FΑ*}„uêuèСU­Nýé$— à à?d•Bª-º'MÿÞ2dHÙÅ5=l/¼¦îZîÃt½†œî«ÓÃ’Aõd0^ËÕr<¥v¼«Uq'!:°Ã@IDATý B¿ã*¥WLÊY•†j)Ú¶bžÉ ~-Ëwóy}À}CnÙb5¼ü¾©ˆÒs¨¨)ŒšÍXÆÙA?.ù_§füj>‘.Ë-·œOC‘¯÷¿ýíoýh¥H«ÝæìåÓ$LJa¥‡ Å>¤a°àÕ:‰(xÞX@Úª)¹Ò ¤‹å›Š#Ù\'=_xÉ%—øA«]ç­Âøä«R/X-sg"gO““rÖùSzûƒS´z}ó<ÖZ?'êÙ.ØþØt®Ãç5y°jbcý$Gõøa}î÷Ûo?§ô0µë¼ÚM‚O••Þ75?´0>U”Ò[éZaXÃb¯éë—&Z€©l33{XäÔ³¼¶“.¡™Y¯t_{íµ—O‰Æ•zÕ5Ø@9{ˆé›O¦÷Yé²Ô4°T±VJΞØ¤­Ñ¼ö@Õ¯wàÀ΂E‹¿eiÁ4Íj;Mӈ駟ÞwÜqÎldN¯g¤= ö†Yßý°>5Ÿ´‡nþ<ª9i²XGI¾¹cr\Îë{ÖŸõÚ“› ê{úÎ;ï8»éwº—PÉPÔüWMn•îE鑲šÙ†yK½ÚOaB»ô5",î+JÝS„ùª}Íóïeµû5Ÿîét}H—zÔ[)óTìÁˆ³eéUûk§šÍêÈ~°5­-Z 1‚{ƒƒ € PQÀ²'øô+¥fTjÌd )D“㲆•Ä*@:«­ìׯô„JM¬)V©É§ÍK.—õ›69½Ü°~+Zo·úê«—›­`šR÷Y 6§{jík¹¢éº7¬T”::Y´-«LêS7'§Yýø·¤Ò†¢ß½Vù5¼-ùÚêÎJÏhëxÿ§°ÊÆ>M~Ç—úgÙÜRK-/gHœRüÖRt¿®íé7.¥„@g=U¨'¥ŒÒrØnûäÿÉýTs·àVr´¯):B5jTÁ´PÃ]½—*á)³JÍWÍG=ý³|WMZž|òÉxžd wÕ¸Í*–—ÞK©9Z&¤ÜPz˜PJÕb³ƒ~}0³æöZK wÕ8«¥TãÛȱVªÅVëçÄ'Þ]ŸËr=Œ'[,Pýã¡–ëÛŸdþ]tÑE‘O3ÿUûô¿ÙMãÂ^çÑÌ,ÔÄǯ°ƒ *h­¶¯k@ø‡ùõš•ª%ff:gÿüç?ãs•ܵ0Òç;ÞFÍCK0¿Z÷¨µJº¨ét+Õü×w-]ÔŒ0\KÃzõºñƧg-z¯ÏGøŒ…¿9ašVk±‡E&j¥¥TZéÚ¶i?Õr/Wòüž¤·«mÑTðÿUT#ZŸ]5±µqÚ:}NôùUÇžéR®zz^½W]Sôw~£6Š?CYŸárën‡{ƒd wulZm©ÆÈr²ÇvÖWEµ«Žç ×ÔtË‚zï![éÞ >H@èáw•bYt¿~KèÕúŸ*»ùÝvÛ­`~Ýs–+Ö¯W¤Œ ÉmT3¬LÕ”t wÝ¥ÓU³¥dLî—†«Y,Ò|Éå²j¸§[\+E£Š=(X6¬GǤ´‚á½^­­²ûÓ.Ί$«‘áK/½´¬ kð5Ü[¶ÓTû øfzM×`Tç©êOA¼ñÆnþùç×l¾s7ÕUíOu~–UÔQj=El]vÙeîÁôö%k›«¶d¥R®Ö£–­4½ÒúÃty¨è g+uˆQ®Ôã›ç±Öú9±€?<ՒΪÅŽ]5ju®Uã˜Ò! µÊ°€cÇHÒyHÿ f¨â¾ Yµuµ¨žÊªëꉧõÚÕº.WóZ˪†rº–²Æ'‹jb«þÿøÇxôsÏ=—ÙšEµt] E&Ö×S‚¬¢ÚW\q…ïÅôų¤¯G4övÆ3Ø€jwªƒÑtQmPÕÐ?Õª>úè£ãY¬ žß–:ãQÑþ©ÃŸtÑ~YÎRo¤'ùqê„S…>þøã~ºvÝ;K©U0¿:=´u=µö‚Zfèó¬»ì²‹ïtH­Ä¬‰ªVu2UîÚŸöÕ{}¿Õ1¨:œR‡Qv çgÓötíPÍ*yþ½ld+ÝÔjŽSŸÕZÿŽWsµ®“{ƒjT™@ª° M¾öº~?uVQ‹Neœ¨µ<óÌ3ñ"Z¾Üï±xFÐo>ͯŒ¥ŠæÑï?ývRÑoDÕÐNÖ`O.k=| ãä8µ(UÚÕ¹ÔñT;ÞdT;+óU)Pû7§Ê7:›šS¿öÚk~5逛š>¨Y¾šOX'[>5€f´š~~5íof¹ãŽ;|³rôåW€D 5ÙÑ—]©ô>6sûÕ®K?ÖCoÔé OµëÈk¾dP+½z|[íXC]Mµ´oéÔᘭ¥Áö€Ñů dßtÓMÎ:†lhO,µÎ”ýyý¡W* Yä׿tZ“RÍù,·_pÓ©'õRÁö°Ã *)0­t0*j†Ã<åš™…y²^ÕÌL)_BÓ2›†+5½ÓC€¬`{؆ÒýX§ÕÎ:ÃŽÓúX gûÄÁt=ÐÔƒPä©ë{úaB˜^Õ;»>(ŠRi\xá…ám§¿yä‘™Áö°#ºÙ w³\x%îa™Î|ÕC)ýë EÁoÝÜëAšþ¶—*úÞqÄþ~àÐCõ†¬USº§jŠ®V‹=N}¢ë„RÚéžB÷júç?ÿÙ?ä¯f}•æiµ¿—aåhl…·¯õ©Ò…Š®…¥þl(ç7Üä Ìê@è!úMg--3Ó“†ßžÖ”KV íJšd:ŦtßWMѼª¦¸I©¢{<ý†R¥&¥TUzÝófëðht©€{»9×Zá§"1ÂZ‡&Þ1Ø – ¸+h‚ÇÖÙfѱª–{2஽jUꋬ Q³Êûï¿ïs+Ø®üÎ ¥k8¥ó-7kÛµ®GÕöùå—}ЦÜòºËWùc 8–ÛN˜–•GUÓêõmµcU DÅš[¹±cÇ–¬Q­ %¥ë”·WÁµzžÖ‡½×÷LùÅõ<«¨Æj-%ýDÙRgUµ¸‚s§Ÿ~º{øá‡jw+ ,ºN&‹¥1ñÁîä¸j‡U;¿\À]ʬëuzý œo±Åq \7GÚOÕ|WQ>Îô^yñ•þš¢Üìz] „ve)•£;ìS:(ö;Lçµóþô§?9}•?2ùÀ§Ô(H®€»Šuž^uÀ]Ázå×gÚ÷ú\•éÏA©mÖ3¾Õþ^†c(¯×(Ô˜×õCµžÒ­¦Â¶õ·:Ü»©åYµ?ÃòÕ¾roP­ó!€ €@9KyÇÇ4ŸîqöÝw_ßjY•c½¤âbjy©¾°”?¾«Š~*#…ŠîËÔÏN¹JYa?5_¹`{˜O­oCÀ]ãÔçb(²PAÆ ó£üWE»PÔj8]I-Lk7çe—]6®x¬cXuÕUëþ­¯Ö ”æ ´dÀ]AXëíש>@ ̤‹šprÈ!¾¼5ê @5¢4o©æøéuTó^µNCà_) ÒÁv­CÖV)ê„U@¥¾)UÔ ›jïi¿:ꨢt¥–Ëc|#¾­t¬É?z‚ªN÷²Š‚¼”®¨·i\Øó<š™)˜ŠR›X¾ºð¶âëá‡îô/«$›óeM¯e\ú¡@zYÕέ¶è&'Yó\AöpO?¬(•",k[JÅ¡ [h!RLdÍË8’úœé3®f©jª«Ž®Ë=¸%VÃûr¯JÕ£¢Î•u/“Uš}_ÑJ/³Ž7=®^£Ð½Ö§û =\Í*êÄ]÷ ú«R^wî ²ô‡ €µ¨·b<¡Ì=÷ÜîÑGÍL#<í´Ó:ý«åÞ4¬·™¯éÔ«gœq†O‡ZijåYMQ`¹TY}õÕ}Œ+ÜÓó)–ÕrºÓAr¥¤<ÿüóªX˜öâ}ý“Ô¿h>Kª°òÚ*订§rái]r‹ Hm¿ýö~”ÒÊ„t2¿ûÝï’³5<j$«iKß¾}3×§”¡”jƦçýªw* ^)qV¹úê«ã‡ÖYkÖ,6®ßV:V¥ÇÍ’tÓ“åtQ.WÕf¤´·€ð•kΧæÓL3MÍ™ Þ©Y`Öu¯æ•ÚÙÌ,]›¾Üþ*g}²$[Ú¤µ^W“M›å˜ÜW†»§@¸†¿ýöÛ>¥S¥£´Î?ý,úŒ•jÙ‘õÙ ÷J=TÓ'µDQÉZÞO¨0-Ì^[éïeاr¯õ©%LrŸy晙ͮU»=¤ÒµfôÝPêX¸7(%Ãx@¨V ý»I÷¬¥Rö*=åàk¿W»þ<æSjTUlEqÝ•jÍ«Ê. ÊW/Qši=XÈ*ª¥®Š`áÞ>=O©ñíè¼âŠ+TÔS%¾ 6ØÀ7.}ØEïu.^|ñEß:@ñÃZ‹¶¡V ¥Îi­ëëŽówz wå”J>ªÒµè¦õìs²ëÇ~À*‡q¨ñ˜…¯´2ªu®æ#z’£ü”ö¥™%¤1Píy¥)Pç‡!€£œÃJ_ }E㺲ì°ÃþB¦Z­ª9§€À&›lï³Ò+œzê©~õå´Þ™»rwã4õø¶Ú±ÊUo}¾u‘ÓçEOÕ¢@5&Õé¤ZK(Û*¹Õºôä·éÆójf¦ë—}*ê8SOØKÝHù™ªü¯3›™é:®¥Zx„]VSA¥ KÝ4…ÒB„÷÷ß¿¯Þ—{Õ>GÍ—^W¹e™Ö³”JF­WÔJIN•ÇP9øÓ5tÔ‘¬~0询¿µÉF§]úªs`= Sú=¸×ýƒî+tC¯´5ª$ì|XŸu¥“ )•²î)Ê­[ÛÎ*­ö÷2k“ãê5Rš0¥ØRºßQsâk®¹&~ð¨ûMS]_UtŸ™wáÞ oaÖ €@× ¨åþ¹çžë+>&÷F”úxÿý÷/ÿõ¯u·Þz«OK˜œ_ýªÅÞàÁƒj²«¤k1ßxãþ¾R±ݧ*È®À©Rª<ÅÓÒE÷G#GŽtë®»®O™bZúí¤Ê«ÚÅ-BG¤aùË/¿Ü©ûdѲK.¹¤8p ¯,›œ†õì”SNq‡vXåciº/Óý¯jàëXéoôÛPÛÉ úª"®úIRëhõ×¥Ê*ª ¥Ôª²MU¤M/T&]JÜÛÑY¿1TYÇ:šUÅþýûû´•ЧÊY5úUÁO¿o^x៬ÊrÉŠ¢ú¤{èjŠ~[¨¿7}Ö´~ývÊÊLRͺºõMÕzÔ5A©¦è©U(öå ƒM{Umä»ï¾Û×XÖJUcM)?°šƒë ¢jºÙ`¿Må] M¡ýˆ.øO5EÕ”DêLCM=ô4SOÕA„Æëé¡Ü»º4êÛjǺÇ{ø§¨Ê«'ÒêðCéATã]9Äô9½?[§«ù[nûé?õ}+Uìjí­U[@i¨ÒO·K-×Èø<›™¥s9«6„j‹«y]¥¢§ÑJ] §òé`:»™™ž¬ë{ Ú]{ä¦Ö6jå¡÷!¿ºŽKפPS8§:YN×’W­ß‹/¾¸d“5ÕXUm†Ð9Ö¥Ú®ûí·_Xm·yíÌïIOk*¨Z1ª­š>ª¹£¢¿ëú\©câW_}Õ§yQë0uF¥Fúœ¥‹jô¨³ß™fšÉÆ“ÓußrÎ9ç8ý Pú£Ç{ÌçáÔwGµáõ}Q‡ÌZVåÉ'ŸŒk¬è}¹ukz©Òj/Kí§Æ7j¤kŠîÝ”£S÷™º¯Ô9TkHÝ»©FŽ®±U¸7è,i¶ƒ@{ ¨e¶R¤ªåMúŸZSUS”BT÷zú^‡jÐR@ júþÑožPYÕŠ~ë&‹îM›•—=¤oÖúCl"¹­j‡u›L‹š^N÷`ºWÖým3ŠjÃ'‹:NM——Óxý¦T ÖdÑ}`²uirš†ÛÑYû}ôÑGûþѲrÓkz¥¢ß@»ì²K¥Ùüt¥JN}¦”µ‚’h÷G á©–jpç]ìbÙcÿOµàÒE5ËTãºYµGÓë¯÷½jiªÅ€ "»(Ö»šÜ—k†o««jþÛàØî£>Ò_NÿOÇ›Giçîªý|ôª'ÒúŽ1"²€IôÐCE–†!4hPA+Í«ý¡Ö€=\ŠN:é¤È~ÈDö‡Ø×nL®×ò¬ûñšþ­nµW-ß]dM°Jž}¾’ëQmnköÙšHû®Úͪ=o߸eCr~ «Õ‡ý1‹¬éWQ-pëS¡`ýš_µ]µN»Y‰¬©_4zôhÑwÚ‚J¾ÅMzªéž,ªY«}MΧZò뵦v±¯uìYgÊ~½–¯` ì'WeÕpOn£šáR­_ìæ­èk}öPÁ×âR«k6ék†©¦ªj˦·—U»]µàUßpÄç>|¬Sì‚u¨Vs˜¦WÕ4±@hdk8èjªhjAܵ~R-hµnI–>øÀ×D ­£Â2öp.Rk¦tK…ä²Íúž$×™5lA€øsl7¦‘5{Íš­[S }?tN¬ÿÿ·´ÑÖaL×+]¯Õ Å:÷-:çú{mbEú»‘Giµ¿—YÇØ #Õn²‡¹Zfí{¹q]qoPn˜†]#`uÜ3„{½ÚÃߪvêž{î)¹ÝSPè©YÃ]ÆV#²ŠOE¿¹ôL-õ6YŽ=öØÈRè}ÕB~£6Š[F&—Ñï#µâ -è“׌0ܯ_?ÿ[Ù°EëVeXÐï¡Pt Ño NÍÖ™~UÖ«˜âo‡õTzÕoWµM¯+ù^Nú-:Çş}Ó>j_“E÷Ê–R%ž×ÒÍÝO+†Ü†~‡W*íì¬:F£äqg +3ƒ²wXº¡š~odµF·‡¾•X{Út_ý—ŽÚðÛ²h×õ”Ï~¨:uj¬¶<vº{\tÑE>G»òdYÐ-ó Î:ë,g)$|'myåqWy念´³@Mæ~´êHÕP'Ê%WkQmQՈדlu„—•¯­šuªs D9 ¾fήÚHÇÏœVëHåk¶›‡x1=µ·ô>_<²Æ{Há;–C²ØßçDr\­ÃvãꟜ‡å^yå_³>¼W-†jÿ¤Xj_Ë·\Ë$ÕJ•‡ZŠÔZÔ!ôwÞYT»X­¨TÓ¾‘¢¼Šªé¬ã E5%,ˆÞ½Ú±¯A¢VG*ªñZ®S }ÆJõaÒ¬ïIÑN&Fèû£ïQ²¨…ZGQ@ zV¹7¨~™:K@ýt¨åô;ï¼ã7©{Ý[©¨µ•äýp¥ÿÔw—ZK©v¡r9+´ŠZØZ€ªÒâLG [ XºN§¿Á–RÆYÊßnuŒºf¨åž2¨f±¨}öýFYa… ~£´Òëw¢~gë:§ª)=ûì³ûÖ‡–þ¯àwe+ìw»:ËN}ZE=ÿ›U¿[{Rÿpúgeü?}n” ¿ž¢¿_Êà ¾”÷]9ä“¿ëYg7[f‚yô)nÝFG©ÎlW m›m¶i£=gW»£€‚§êlOA2ÝÜ¦Ó èBš°+=¥X@|u£t ú#QmÑ ýH Aæf4+pW33ÝÈè§Ñ’lΧuõíÛ×§–P°XÁæZ‹nXôÙ ÉåÕÌL)*ôšÕcrÞ¬ájš™©=@Ѓ%uÂ’U´oJó¢fÏ!øœ5ŸÆéÆËjø:Ë«ìÓd•š/9^M4ÕâÉ'Ÿ\l×||6ÂvÔtRM(Õ ¦ŠÕF¯x³¢ýÒ|¡(MI¹R.x߬ïI¹í—k*Ž»ÜòLCÿàÞ€O”PÓÿйŸæQð=ÜK-“5^&†¾uÿîYó2Ú_@©[!=p­’ È*í®þ©[«—vu–«:SMþmh¶µþ~©ƒ_Jy¶ ¸+˜¤|¦ F(ªŠj g˜Ê:Sh®À^{íå”GQ?®0VZÕ4WdÝü*§¬òÇ*Èh);š»ñn´6åMV-†•ƒ·TQÐO}=èa›ræ+ Š~h¨o…ÐÛz_îU7 ðnµÕVeŸ®«¹¥hqª1®ZY½Àk;zr¬Þ»UÎåd™gžy|ñY õÔÙR†ø'ÆêM^×»JÅ:UôŸ7õÜžtH.§€¯j¨ËÊR­8kÖX±6ºŒ•^½Ù«&ºZM”+zȤcÒ?õ#¡'ÞÊ׮Ͻj\¨õ‚Z„ÜÔåÖ¦éó ïz>OÖü ÓëZ&XçÓþï¾¥Š¾—Ú'õÞ^.¨µ¼j©+Oª˜%ƒÎ²ÕþYg¦þ‚ñ¡hšÎÏ®ÖχuFûš6š¦¼àÉÞáË}6â…m ß“äúÒÃ[o½µ³T=yuS•<îô2¼Gbî ŠMÓžºßyÛZê¬ú'ÒýŠ¥è벃ÑÃoõ›õÙgŸùÊWjõ\ê¨ÜNŽ?Þ©u¨î×UC‡ÕÇTµÅšÖû–™¡U©4 &龤]K«ëvud¿@z¶@Û¥”QPÃò,ÇgMP*(§À®¸ýöÛ}ºŠ?þ8sW¬—nw饗:ËÛœ9½#Û9¥Lúø°UMo5çRºË)î;9QÇ›ê ´«ÌtF33¿~ܪ‰°þ)½Šj²ëG¡^õYZd‘EÒtß7£™™ja)ÍH(êèT›æY,o»SÓiÙ+P­üúq«šõ•jÌç¹_]¹î¼¾'4ìʳʶ»“@+Üt'OŽ¥s”æOéÔÂO)Í’EAn=TÖCvUvH¦ÉSz¥OÓƒeµUôà^tUi!Ýç¨"€*„õ+èmyŠ‹Ò…jºõ©á[±¥[b©ò•jLªõ åñu–8l¢èU-ÎTiB•3ô!™O÷–JÅ¢JÖw³¾„|äJtOª‡ìªd£ý!†y°W:‹Ã;¬¦{ËÛ§€©%¥LØ®^UI!¤¢©5¥L½ç:¹}†hîœR¦UŒÙ()àSÊ´]À]75ºÑÒ‘nðT“’`{ɓ̄.P­ÕtW`P7äjn£Ú½ LZÇ‹¹ïQw ¸çŽÅ芀{C;Ì €@ tõ½A6›msÝÏn¾ùæEå¬ÃRj”[n¹%žd…;¥ÈË*ªZ»©5•ß颠{2¨®ÖcJÛ8räÈô¬™ïõ›qñÅ/š¦Zñ;o­X41c„u|îÓ&')_­¤U* |k«mÖ•÷FÎu%¦#ÐÜ»Bm"€ÀÏí™Ã])BlN%­( ´úq¢@@€{>í& êÛo¿}A°}¾ùæó- 'Nœèžþù‚NÞ“ÁqëÊ+¯ìÖZk-Ÿ—<»‚ìZg¶k¼Þ«£nÕp5Í•¾ï ƒ ‹ùWõ“ ¶÷éÓÇç§UúÕRWÍn=Ø Eû“¸«&ºjØ«RL( ìk>¥TJÃO?ý4Lò¯Y}ö,µÔRqÀ]©•Zg¡…òµÙß{ï=¿ŸªÿÄOø”sJÚÊ¥ÑsÝÊÇÆ¾!€ ÐUm—ý« Ø. € € Д&櫯¾ò‡ªÔ1 F'û"QZ–;ï¼Ó©ÿå1Wà:Y”Pý¥¨ï˜[o½ÕOÒ¼JÅ’,J8lØ0Ÿ"O_-§ÔÉ¢ |(\pS;»CQ«R¥ÕºTÒû£q§žzjlW T¥ÁQÇì¡(à?zôhŸÊFÁòRE)vT]A¥öK§8L¦@•[«—FÏu«û‡ €@Wpï u¶‰´±€:/½é¦›ÜÓO?]pW_}µÿQ­Zbª-¦fØjÎIA@öP_1¡¨–÷Gás™+E¢ò›÷îÝÛmºé¦îÆot#FŒp|p˜½àUÁé¿ÿýïNúË/¿ÜçkWÇòÉròÉ'»lWº–t°]ó&÷GrõE£ĵ?Z_ß¾}}çêªm®T.éÚíZG2uÍ•W^éïS4>Ý¿èØ®½öZ¿^ÕR/ÕOÎòË/ï”ææ¥—^ò·ªF½:Wÿ:Éê§ÕKÒ¶‘sÝêÇÉþ!€ ЙÜ;S›m!€m. ÎJU«,tl–<51×?•|ÐÿSsrýx¥ € €@û(ˆ­€zÈU~ÕUW9ýSQ`z«Ñ®´1ªµ^.ݧßJã¢NWTW‡©—]vY ¡t0Éõ<8ž–Øk¯½| \icVG«¡¨“Ò¸5×\ÓuÔQ™•êþå‹/¾ð‹,½ôÒEÁö°.½ªï%Õ†¿÷Þ{}ç­Éi·j_Û! žÞÿôûfëôzy €@O˜¤'<ÇŽ P›€òWÛQµæK7³®mkÌ €]%pÍ5׸ýöÛ¯¨ÓO¥^Qî¡C‡úÔ*»ì²‹ûüóÏKî¦j¹‡ô/ýë_}ð0³j·‡ÜëêxUµÖ³Šj­ßqÇNýy¥‹Rß<òÈ#îÄOô5Ûï¾ûîô,>ç|©€{¥¢€¾*¬´ÒJ³*Ø>pà@÷»ßý®[ÛÃÁ5ë\‡õñŠ €@O †{Oÿpü €@ sÎ9§»þúë}ŽTåüTñtQÍ7ý V³pu°FA@ö˜nºé|§ŸÇ{¬Ïᮿûê,õÕW_uo¼ñFœfÈ!NÓÔi¬'VŠªsT¥jQÇ¥ª®¯Ö¡W=¤W`¾\YýõÝzë­ç;#}öÙgþ)¥‹j¼øá‡~Ñ>øÀm¼ñÆîæ›oöùãÃút,¡„Üôá}-¯êõž{þùöKµÄ¿üòK§\òo¾ù¦ï6ž© šu®ÛàPÙE@:E€€{§0³@ û¨šþQ@@ { ¨ãÒ#<Ò§Œ9äCüCt=HEµÒï»ï>·Í6Ûø|ê ¸kijÊqÇçØ+à~Ýu×ùuŸvÚi>¯ù•vf±ÅËZÔ?Üßu×]} [}Ȭ°Â þ_rf¥v9ì°Ã|3ÖYgÜ—[n9ÿ0@5ÔÕ«‚ãêØµ\QM~åiŸrÊ)ýl:fõcŠ}ôÑE´/ýû÷³µük³ÏuË0;ˆ €@'R¦Ù € € Ð.ê˜ôÉ'ŸôqÕ`O·hSй 6ØÀ-»ì²ñ!…ZæñˆÄÀ ,àvÞyg?FAï=÷ÜÓÞ5BµÛ?þøÄÜ…ƒÏ<óŒ¯ÿè£:Þ•=]æž{nŸæ%ŒOï‹:y]j©¥üdMSîyÕ†/UT‹]­õÔ«jÒ«¨&ý×_í‡gœqFŸ/>Y£_zuÛn•š}®=ÿ!€ ÐèáÞÃ?> € € xíµ×ü[ÕðVŽvÕFWu¥ŠSÀ\©S”m¥“Kç;O®OÃJM£´2ª)®œë¡(ÝL¹Öþhþûï¿ßwjºîºë:ñg™e÷é§Ÿ:åGŒVéVYe•x8 \rÉ%nµÕVó5òµïóÎ;¯h `gŸ}v7nÜ8§N\Xúé§Ãbnøðá>X¿à‚ º©§žÚÝ?ûì3ßQëꫯîfžyfŸÚF(ÒTó_°n²É&¾Ö}X©:UG²}ôQåk݇7j)0ÙdÿÿS]A}=Ø8ýôÓ}Žú0Ï‹/¾è äŒèœ¨¨A(<ð€÷ ïeµí¶Û:µ6H–àÛÌs\?à €ôD^ö$>ê‰Î1#Ð]^~ùeß$W7ÕÉ›øîz¼ € €@sðU ¹Ú¢.J=S©ì½÷ÞîÒK/gS0ù•W^q f—*?þxÅ`~rYåU×2ºN—Ûn»Í×@OרOÏÞk] 䫦»ÊUW]UP“>ÌWÍë˜1cœÖ÷Å_¸f˜ÁÕú3üàƒvgŸ}v¼)yŸsÎ9ñûjô`D ¦Ÿ~úxö¼Îu¼è}÷Ý×]tÑE¾õL¥þ!º`÷Ø$to ö÷¶)eº÷Iæè@@@šŸk®¹ÜZk­›³V°Î:ëøU« ¶kùcŽ9ÆM1ÅñªvÚi§²ÁvͨšõJ_£´1ÚŸPó;^ÉÏJórÊ)§øÚîYÁvͦ<ô=ö˜Uð¹TQvÕB5jTÁñï¾ûî¾£Wu"ŸU à?üð‚I ®ë8lWQ¥ûï¿æ‚~£”;2PíôdQZ×ôjŠLöÛo¿‚`»–Ëë\W³Õ €@w †{w=³W †{=õ8 € ÐtÕÄ~ê©§|§¥cÇŽuSM5•O-£”. ‚×R”be饗öµ»8=z´_W-ëP ™'žxÂç`Wç§ýúõóëPZí[µEiq”>FœªU¨ösÌ1‡S0]ùÛ˲'L˜àƒñJÇ¢T, ÐkûJÓÎ¥™çºØ÷ö †{ûŸCŽ6ð5ÜÉáÞÆg]G@@òPMðå–[Îÿkt;JíR©¨T儯µÌ4ÓL¾Æ{­Ë¥çWš˜*&=­Ò{uºÆkø•æm§éÍ<×ítÜì+ €Í¨®ýY³·Êú@@@zŒ€:6Uu•É'ŸÜ§—é1Ï"€ €@ àÞ£N7‹ € €ä+ðÃ?¸Aƒ¹W\ѧgQŠåaEédöÙgß¹iÇ+ € Ð]H)Ó]Î$Ç € €´€ÀC=äN?ýô’{òÍ7߸üãîÛo¿u<ð@Éù˜€ €´£5ÜÛñ¬±Ï € € €@‹ ,¿üòn™e–qÊ ^ªÌ0à nóÍ7/5™ñ € €@Û PýmO;Ž € €´žÀôÓOïžzê)÷å—_º &dîଳÎê&™„ú_™8ŒD@¶ àÞÖ§G@@ZS`Úi§uúGA@z’U zÒÙæX@@@@@r àž-+F@@@@èIÜ{ÒÙæX@@@@@r àž-+F@@@@èIÜ{ÒÙæX@@@(ðã?öÀ£æ@@ +¸w…:ÛD@@è /¼ÐõéÓÇ-¼ðÂîÝwßí”m²@è¹Ü{î¹çÈ@@@n-ðù矻C=Ô}ûí·nôèÑîꫯîÖÇËÁ!€ €@× pïúsÀ € € €ä pà 7¸‰'ÆkžwÞyãa@@<¸ç¡Ê:@@@º\`È!ñ>ôîÝÛm¶Ùfñ{@@<¸ç¡Ê:@@@ºT`̘1îñÇ÷aË-·tSO=uüž@@ îy¨²N@@@.Hçkße—]ºtØ8 €ô î=ãÞ~ýú¹ÕW_=~Ï € —÷¼dY/ € € €@—<ôÐCîwÞ‰·½óÎ;»I&áço  €ä&ÀGn´¬@@@ +®¹æš‚Íî´ÓNïyƒ €ä%@À=/YÖ‹ € € ÐéãÇwÆ ‹·ûë_ÿÚ-´ÐBñ{@@<¸ç©Ëº@@@:Uà–[nq_ýu¼M:K)@@N àÞ Èl@@@ s’éd¦œrJ·õÖ[wΆ٠ € `Üù € € €t wß}×1">–M7ÝÔÍ0à ñ{@@¼¸ç-Ìú@@@:E@µÛúé§x[¤“‰)@@N àÞIÐl@@@ _¡C‡Æ˜uÖYݺ뮿g@è î¡Ì6@@@rxì±ÇÜèÑ£ãmì´ÓNn²É&‹ß3€ €t†÷ÎPf € € €¹ $;KÕ†p§ € €-@À½³ÅÙ € € €@S¾ýö[wóÍ7Çë\zé¥Ý€â÷ € €%@À½³¤Ù € € €@.·Ýv›7n\¼n:K)@@N àÞÉàl@@@ ¹C† ‰W¨¼íÛn»müž@@ 3¸w¦6ÛB@@hªÀرcÝðáÃãun°Án¶Ùf‹ß3€ €t¦÷ÎÔf[ € € €M:t¨ûá‡âu’N&¦`@º@€€{ ³I@@ÿcï<à¤(²?^§w¢žžñÎ,ž9+Š¢‚EEQNÅœÀœsΊYQ f ˜#&<ÏœÅO1ü=ûÿ¾uV]MïÌîÎîl˜Ùßû|v;UWW»g¦ûիߨ k¯½6V4Ë,³¸Þ½{Çe͈€ˆ€ˆ€ˆ@kýµ‰ëx" " " " " " !ðüóÏ»_|1ÖÕ¿שS§¸¬hmr¸·6qOD@D@D@D@D@D "®¾úê‚z$'S€C " " " m@@÷6€®CŠ€ˆ€ˆ€ˆ€ˆ€ˆ€4ºí#GŽŒ•,¶Øbnå•WŽËš¶ ‡{[P×1E@D@D@D@D@D@šEàî»ïvŸ}öY¬càÀq^3" " " "ÐVäpo+ò:®ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@“ ¤r2SM5•Ûn»íš\—vJýR$Uˆ€ˆ€ˆ€ˆ€ˆ€ˆ@«øúë¯Ý]wݵÞzë¹ùæ›/.kFD@D@D@ÚŠ€îmE^Çh믿ÞýüóÏq_%K(4#" " "ÐÆäpoã  Ã‹€ˆ€ˆ€ˆ€ˆ€ˆ€”G •“™a†Üæ›o^^*-" " " -D@÷«jE@D@D@D@D@D@*Oàµ×^sÏ<óL¬x«­¶rþóŸã²fD@D@D@D - ÈáÞ–ôul²\yå•å%'S€C " " " mL@÷6¾:¼ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ãüöÛoîºë®‹…;wîìÖ\s͸¬hkr¸·õÐñE@D@D@D@D@D@Eà¾ûîs~øa,;pà@÷‡?ü!.kFD@D@D@Úš€îm}t|F¸æšk Ê 0 `Y " " " "ÐÖäpoë+ ã‹€ˆ€ˆ€ˆ€ˆ€ˆ€4Hàûï¿wcÆŒ‰åºuëæ^xḬhäpoWAm¨—ÀM7Ýä¦L™Ë(YjD¡vD@÷vt1Ô⮾úê¸aÚi§uýúõ‹ËšöB@÷ör%Ô¢Þ}÷]÷È#Äm}úôq3Í4S\ÖŒˆ€ˆ€ˆ€´l/ Q;D@D@&e™>|¸ûå—_bá=öØÃM=õÔqY3" " " "Pkˆnç9(˜äd ME@D@D@Ú9ÜÛÛQ{D@jŠÀøñãÝ7ß|ã¶Øb‹Š8ů¹æ·Ûn»0=z¸^x¡àÀóÏ?¿îDJ/ %óÖ[oÅÛo¿½‚ " ͈€ˆ€ˆ€´7ÒpooWDí¨?þx<—t>®,sæØcuŸ~úi™{©¸ˆ€ˆ€ˆ@[ô[ó®@š,•šp¸ËD@D@D@D ½ý½^µKD ê üüóÏñÒù¸²Œ™W_}Õ{î¹q©¦Ò×w„¡h瘻ùæ›Ýšk®ÙÎ[Úþš÷ÓO?¹Q£FņuíÚÕ-½ôÒqY3" " " "ÐÞÈcÓÞ®ˆÚ#"P_ýµcøñ3Ï<ãxQ«vÛgŸ}ÜÿýßÿùÓèÕ«—›{ý”Ô~è0f™e·å–[º.]ºt˜s®Ô‰Ž=Ú}ûí·±:%K(4#" " "ÐN Hý^5KD |¿þú«;ï¼óÜ™gžé>üðÃXÁÿøG·øâ‹ûĥÆ sÓM7]Üfî½÷^wüñÇ»W^yÅeYæþð‡?øè©#<Ò­·Þz¾Øž{îéî¾ûnŸ•3Î8£OØ5|øð¨#Šs—]vq=ôûꫯBõîœsÎq”ËÛb‹-æhÓf›m–ß—o»í6ŸhÓL3;묳ܺë®·kFD@D@:’…ß×9æ˜Ã-²È"n†f(ëÔé¼F œä¥üŽ/´ÐBîoû[YuP˜g…÷Þ{Ͻþúë¾ óÌ3ëܹsÙõh‡† ¤r2úÓŸÜ6ÛlÓðN*Ñâ>úè#ÿ|šÊ0ÀÍ;ï¼-~즀< ?ü°›2eН‚çêý÷ß¿©Õi?(I@÷’h´AD š<ÿüóއ|æyÃÿòË/û¿k¯½Ö]sÍ5®[·nÅÎ8ã ÷裬ãü /ôw^¨/¸à‚‚íDÑ_qÅnçwvk¬±†ßöàƒºë®»®  8â‹EÚ?ùä“î”SN)épgŸôE€y 2èþóŸÿ8®tŠ¿óÎ;§<í´Óú„ä“'OöŽó‘#GlOp žzê©þÇ}j< 2¤QŽ\œõŒ:£cýûï¿O«q .¸ Ûj«­Ï$sÍ5—»òÊ+ ¶—Zxê©§Ü¡‡ê^|ñE÷Ûo¿ùb:uòÏ]t‘ûë_ÿZjך_ÿñÇ»ûï¿?žgïÞ½Ýì³Ï—5Ó6è¸Zi¥•×'5ž‡Û«ÃQ¯+¯¼rÚ\ßY–>glÔ‚ˆ€ˆ€4ƒ€$ešO»Š€´¼8oºé¦Îv"Þzôèá_ĉ` ÆËz¿~ýÎòÔþñ8¢Óò¶ï¾ûúU‹.º¨¯+ÝN´M6ÙÄ¡%líµ×öë8&QòÁ˜Gw=ýcÿ%—\Òíµ×^¡Xéé§Ÿî£ùØ@û;ì°:e´BD@D@j‘À/¿üâGt 4¨Ž³ó¥SšQ`8Òn¸áß±^Œ£Ø–]vY?.ïl§<îýû÷w}úô©ó|Ödze–YÆÝu×]uœí”#úþä“Ov磌®r“&MJw/9Sýp_~ù¥?>Ï(Ÿ|ò‰»å–[Üí·ß^r¿Ž° :]‚IN&hÛix®mÛV”wtå?*—J‹€ˆ€4€îÍã§½E@Úm·Ý6JÈL?ýôîÎ;ïô/¬ãÆscÇŽõó$+ûË_þâ[ËKl>š¥oß¾îÍ7ßô‘ié)}þïÿÛ×ùØcÅMh±Þwß}þE˜(´`Ÿ—ãï¾ûÎ]zé¥aµ;øàƒý #/áÈûþóŸþ%?Lf>øàwÒI'Å5§vZÙCçãΚ*#Àè3F›ûóŸÿìG¨ñ»¿úê«;äERËGÀ³íÙgŸõ£È>ûì3_§ 7‰DïÞ½»›i¦™b·Þz«_¢Ìã›3fŒvøñÇýjäéVYeÿ¾üòËûõ´<óo¼ñF~UÑåYgµÎzž'ØxãëlëH+™l¶Ùfsm´QXÔ´ ×8qâD7bÄß Õ†Miô¡W\qEÿÜMí—‰€ˆ€ˆ@Ký%éªn'ðî»ï:œé:Œ¼“T4}&’|ýõ×÷Žp†ŸcDž!“ÛˆcXw0¢ØˆŠÛ|óÍc4z¯O<ñ„QåZbzÐAEÉ5×\³¤c¾%Ž­:E@D@D ­ ü<ØùçŸïg²é6:Áq°o±Å¡ˆ[`â<3tp£÷ýóÏ?ûõŒ*c?"Ïo¼ñF‡ Qåüî‡Qit¦“w%5¢âwÛm·¸ŠÑm8Ó‘…»þú뽄 QéÅrµÄŠÌÐ>ˆœÆh¶§Ÿ~ÚýðÃ^išŽjŒ\ 0!ØvÛmçŸõ²¦mK€< \“…^¸mRÆÑù೜¾'”±»ŠŠ€ˆ€ˆ@£ ¨k·Ñ¨TPD =àe,µ††“ä,ØsÏ=çŠIîN8Áë¤<Ø¡Q‰S?Ú”DÀµ´~(Ú°80: xùOÛÚ£©ˆ€ˆ€Ô*ÔÙÌï ‰éÇi6ß|óy©µË.»Ìýýï÷ÚÌD®§vÇwø©¬C-ð9çœ3-âcùÝçXAâìè´ ¢xC„ü +¬à¥^òÑõŒ¢Ûu×]!*{‰%–(8V~á€pŸþy\MÔ>22ù6Æl9™ÔzÆKËv´y:…š›Pftô‰$>tb¥ÒŒåúá‡ú‘£Œá3Ë´£3Š‘§Œö\l±Åü罜ýCYò;p>$Fæ™?ބ횊€ˆ€ˆ@Ký¥Èª^V!@2Ô`h½"ÓX{饗J&Hc¨9’0?þxAuDÁµ´³uÔQñ¸¼tð‚_Êx¡G3–6ËD@D@D V_ç5ßD”#Ï 'àrË-ç5Þùm.&ËrÏ=÷„â>J}Žì=öØÃwnãä#rþµ×^sÁaN2Ó`ü>çíaÓ#<Ò‘ 'aØ?ݞΧÎövØÁ?w0ZOæü5gÔa0:Yê{ å:Ò”çÃJ$FBé /ô92I<¸‘ñY|ñÅýˆž7×Yg’˜ JÙo¿ý¼ÜL(Dà²Kt pë3œãtDM˜0Áçheyö&I+Ÿ?¤œê3:æøN`ôIú£HH*R}ô´MD ¥ ðËH6ü a^8&#éÉ7'«v±e" 5DÀ†ÞÂY$W UéS¹üòËýùrÎåþ 2¤hÅÅ–ÙÃ|Ñúì%;»âŠ+Šî—_iQw±Ž¡C‡æ7×»lIÙâ¾9/“Ì©·>mj$`Nó¬¡ßD‹ÄÍ,KÓã·1ü†šüKíù¦ Ë›¤\ÜlŽÞ¸þý÷ßë›2³÷Þ{ǺBÛ˜véÒ%³hܦTY“ûŒ=º€Ó©§žZ“çÙÔ“2'M¶ÖZk0Jï§ü¼É(=Ô[o½•­¶Új®Ç$œêÔcɆµ¿É,·RýYaNÿ̤3“z©·.‰šYG\fpEëásn7 ÖøÌ0à EëÑÊê'`¨þ>Øšpz@IDAT°Nšê?AM°|o%¿£¬ƒ³¦ÎµŸÌö;ã¤á™ˆ@ÕH£Ð’$2d¤ù;÷Üsëœ7û3¤û©§žòÛˆ†!q)‰–0¢ìvÞygc? ~]cþ•S–úH Æð×Ra˜;eioõÇ:™ˆ€ˆ€Ô 7ÜÐaŽ^úÅ_ìõ—Ék’F«üñÇ®wïÞnÔ¨Q§<óÌ3Çe¤%2s¦Ç"i2ÕtžDê•2¢vC´üóÏ?ïÌñé^}õÕJU_Õõ¹Œg´Âeÿ#P‰„Â<+3:’¼DÁá³DžžEóÖØDÀìKÂ_¤Ã3ëwß}ç† –¯Ò/ŸrÊ)î´ÓNóÏï¡0´èú`3‡]öÑGmû{ï½—™C.þ®ºêªåL›=nëÖ­[fÒsÛÓË£kMF#ûꫯâæC9$ÖcŽÁ¸¾¾“µ(º9p7‰Ì’´fµN—Xƴسüsóæ›o·[^‡ŒzS£ÎÍ6Û,–¡=ŠpO ÕÖ¼"ÜkëzÖÚÙX.¹Œïnë<Í,gLü^R„{Í\iáN¯LD †t4‡;—Ž,^ŽÃKE§N2þ‘q9ñÄ3Ó’ôËóÌ3O,CYÓh÷Wž—b‹~ÉØ/ÔÁÔ"æ³»ï¾Û—áÅ{ºyèó/(~ûÇ‹@(oÑ=¾ G}tÆß Aƒ2‹šÛ)ǺúŒsÓ¯Ì,".£¾P7†RCtë«OÛD@D@D ½xöÙgãï\=2Ó–.ÚÔ{ï½7–³èÜ‚28ØÒçž òŽ|¸3Mvg‘è>ùȘ1c<)äb0‹€qib®°#IÏH"e?|>yš½°„MqJbµãŽ;Αì,o$L³—Ÿ´‰„T´©T»ZÆÞúŒDl$³ÊÃìZÛ³gÏü&-‹€ˆ€ˆ@Uxýõ×c»Çï—0gœ3}fgÎqgŽm‡ ¿áÁ,Š=Ìú)RsÖñî<ð@¿Ìoð<à©A¦â믿væØ/HŽT û¤Ö«W/gîŽç´o¹å–>!*0æ(wÓM7³h{ÿýÌ3Ï8stúÝoºé&ŸI Ë9ãŸ)H ™7d/Ì©“‚ÚÈ5/¥ä ‰a-ÙËÇå÷«ÕåTNÆ¢ÿE%×ê©6ù¼š›P˜ûõÛo¿õÇç9Ú¢ÊK¶…ÏŸ sè;‹Ä,YŽ ]»v-º}î¹çŽëó²L|f‚ñyìܹsX¬3EÚ1ØÄ‰ì³à›8ôc©„Å<û7”ø5V¤hc¼×ò›Áo•å*qH>‘0¼\Cn‰Dà¼oÚ(gMåVÑ®ÊWŠK%Nª½´…$ÚÈi}ðÁ>iû|óÍפӳfîí·ßöÏ1"Ü/¸à‚´óÔM4[>z>ì4Ì^{í•qÝ2ÓœÏˆÞ û†é’K.Ù¡#áâ¦í" " ÕGàñǯó{~÷ŠMZh¡ Ù‰bfyN¼”E±ýÒuö‚šiVÌ+'¹$õZ˜X£æÒc1"Ü)Ĩ»üö°Ì(¼Žb/¿ür‡]wݵ£œzÙçÙœ„ÂiÒ¾b#1ÊiLáþðÃÝ•Ï`¸Ÿ-H&–!j=µÊ4fÊ~AÚ‘Hþ°Ï•W^ë/6cZòñû@îÅÕÆºjpgTõyççGQ#Áîk¦Œ°â=–QÜH=Y²¿Xü^®»îº™åNÈf™eÿ7xðàìÚk¯-^¥ê´ °¢ïŽHæG‘X±Xõ¦ÆÈn‰‡ã0å}¶¥­)\h“u´gŒíe¤¸©e/¼ðBA“I® —PÎ:ß3ë\ϵ–·¦¶%_OXnN„;>d×Ù“Þ+œ'ÑòHæ6dŒü± Ï¥u0O½ÜsŒ²ïHÏ" 1kÄvIÊ4’Šˆ@ÕèÈ÷p±,b'³¤§^ê'õèÑ£3‹žñÃÈqœ·…¡WÉP_Kô”ñ¢>k±ð¶h›Ž)" " í/€¼,Î?ÿüyP,2¯àe2¼¢yŽV;N´úl„ ÙÚk¯ma¦Ô±ï¾ûfß|óM}Ud™›Ùˆ²ù—´æi'ZÒ•_P—%„ôç’–Oîhn¯³Î:ô8F,Â=C6§£˜F(¸Î>úhG9õ&'ϵ8T,¡p¶Ûn»e–4ÃQ”ÞgÌç%ŠÐ7eúõëפc‡šãp§:ºB[Ê~þùç¾Ç{l¬ge}FÇ\8Žîõ‘ªîmÕèpë­·ÊêØ%Ð C-ÜÓašwÖ‡õé”ýÓß=ýQ§µRp#,¸à‚uÊXBâ‚2•^h*Ú‘æ HÏ€·`0(Å몫® Åü´9m)¨(YhŠÃïþƒ:¨ä³Q8WÎËFDäÕHí%¼ÖZk­:×4쟟Nš4)Ý]ó¥ HRÆn™ˆ@ `ø±ý¸ú¿örz QçO&" " "Ð0¤,J,ˆ„Œ½”:ëÀvæ\w —¶/ ƒ¤KCfÎvgNw/ý‚üîVo#Õœ%fu9ØP¾¼EÒ:{Qwààldÿ³_?ÜŸ!ÿÈÉYt`ºµ Ò6u6Ú ¤i»éÈÆxK8p}¹6²BÜoôÃþ‘-2Mrÿ—–âÞ4gŒc;f‰ù¼R(ƒô Ÿ1˜[ˆÿLñü\Ÿ™_ÁK*Ù¨ÍúŠ•½Í¢eýç‘i³ itA’:‚Yô¾³< a±Î´”¼c‚Z!­HÀ:™½”X*•„„Œ¸væ4õÒgü¦öÆoøÅvÚÉY‚c/³Ægãó i‹Pvßÿ½¯i4Ì‚Òü÷@øž@ZÊFÎ8äJ-—Bؽ`Êï˜åjðß?|µ´5‡ mCrÖœä^†ŽeXòýg#×Yôfù,¼\–ˆåØÀïåùo!ûßܶĊ*0c ÜuäÔ„ü×òË/ï,I¶³\r~÷ü9î¥aÆ”gß™×[ç‰CfÌ‚¢L^*åe£ ü=wÐLýJ;äµED  (½¯šÚ," " " ›‰êíÍ5þµ,«K  …©uÅWŒ¬‘Vœ‚ƒˆY]DÔÚ‹‡3}ʪKZHb:sd8þéˆH!#.\wÓaô‰êž±Öˆ€ˆ@ÛH“¥ò›”F ¶M‹ÚçQ+‘P˜33ÇÈ)S¦øÑ$Œö0ç‹ÿ`T&‰ùý#Š6MJ:nÜ8Eir?Τ˜üh@Šß“'ð#¸†üöl»í¶eÌîó\Â(s ¹Ûn»Í™ì¯†$æøq&%åGŽkÎ$gN3gºË>Êž‚Œr¡}$%6íjg9•œIkø:ˆ’¿á†œéOû$¬$$É2Ñü©ñ<´€ý.Â(PsJ¥›5/­F€¨ñ`$Ù&Ú<5>O$û& ÛòwùÑ)¥’‡ý,ŸAïQ"˜ù ZþwÜqÇù¢^x¡Ûe—]Ân›2š„gÐÆß$#O­\HLnÒY~ÄÏÂ&C瓘§ÇaTÐe—]æWÁšdåy«D[òu6eùŠ+®ðן}I&?räÈ:£ôH.or8ŽwFýpÞÖ‰íLÊ®àŒÌ vÎ9çøßxqñËè;¸ðû€‚#.de(í×j$ ÷j¼jj³´´Mn!F&Ø#By¢W쥵åÑŽkF‹hÁO?ý´h+wØaÏ‹ÄIÕbDAU”Oòœ^{"cH]*Édkž+‘‰\ƒ|«ÖlƒŽ%"жÈ¿“þVuïÞ½mÔŽ^É„Â$,'i)ɉCth¯^½êüðà L´ž$E–Õjr¸[r¼_wÞyç&_„Ôá¾Ùf›5XOšPÔF²Äò©s¹9÷~øÁ®øl5æ/ÿ-¢yhU©œŒ9@]ß¾}[õøÕt0…V2¡0IW‘~øè£¼| Ò 6úÉY'¨—t±NZgΘ:¿%H^äe/ò©i¶Æ2;—…1‡ŸOúG²C’¹"m€´Ó†’$Ûè/aCbdI"‰c#¾¼œš9½ôBÒhL»TFZšI;ƒ!R ³è÷z“!ÛÈN/Óıf™eg£W*qØ‚:ø.oŽUš‹vðïC$–½æšk|ÑEYÄK­Cz†ï»¼Uº-ùú»l^æ…ï·rÍ:5½¬h^:kà 7t={öôßÕÈvñgAœþ»9/ IÒÞ½{{é/Þd$P¯O^E@ªŽ€"ܫÁ"PqD Øc€ÿcˆd>Nþ€$a"z}^xáÌôRóEjv¹¡÷j;ñ!C†øëˆœŒ½P”l>ÛÂð}áµ¥)½-éëØ"ÐöÞ|óÍø›Åïép·}£Ôª&PMîD‚‡$˜Hƒ ±Õ1"„Ѭ©¥î|—š#5ûî»ïÒ"~ 'ÓJß»æH-(“F¸×'åB"NŽS*ijA¥MX¨—ôÐŒçMÄ»åĈì‰Ç—RÌZ¢-á8åD¸³£àΟÐø÷<ÞõóŽÉ”w~o×XcÌ:\ÓMqþ½÷Þ˶Új«x¼UW]5nÓL½|„ûTv‘d" " " 5B€è…aÆù³1g²p&®>#zÛ$U|s|¸áÇ×WÜo#‰Ùĉ}"V’Ž•c?ÿü³¢0Ý@"JÊÙÿ•W^q÷Þ{¯³Çœ:»¡÷ÄOøA&Lpö X§L±Åê*V®¾uœË3Ï<ã£ÆIÐÖXƒm&)Q7Í1"š0{Aª˜Ök/në­·ö«ài/wéæfÍs¾úê«þ™Ž¯O˜ÅýR)kîý3yòd7~üxGtW1ã~~à,K•)¶ŸÖ‰€4@ÝN D9ËD@D £ |ùå—÷§KDqÿþý}Dq©óg„ QØ$´$¹”;Ö'Z=ùä“9ãý³1‰3‰êæ¹#q1ëR#š›'ØË/¿ì¾ùæ›t³aÃèq¶¤µ—#Ž8Â%Ž‘ltРAñÙ›¤Ï$ -f-Ñ–bÇiÌ:¢ðƒvÚiñ]÷½üï&¼_,•çßÔH$M^žw­#¢è;ÓüóÏï…ýBÄ{XÖ´õúäµQD ê(½ê.™,%póÍ7Ç(“ itÝDÊ-Ö9æ˜#c9Ú®ö8‘¡wHÒœ=zèŸB”ô­·Þv):5Ç®×mM“ó؃~fÃ3“¶©³Ïõ×_ï»ì²ËúmhÁ.·ÜrñüR­G“R)ˆÖ¡½áè’Ì3gjÜʤS{ E³RîDƒ°ºÃD m²É&1ùT¨ mL"gJzDuêÔ)¶Ç†Îg$}#êÄ^ˆüz’Ú5ÖH4Èñíe©¨NcZÏ矞¡ÁËß×_í7…ˆ0¢•ˆæ)eè=r´.mÈ,†N|580}öÙ}Ž9ËcY4|Ó2é<÷DÞʽÌ©îëGw»üòËãHŽp¬E]4¶ÿµ×^ËV_}õ¨ãIîm{±)™T7ßF-‹€”G€ßšô;ÃdCêS^í*-"ÐQ „ç™jHšÊ5²€ Ÿ!<ŸðlÈó7yyÌaž :4³@‰l…V(xv"j°|„{¨«ÔÝrsÞ‡Ý ¦ŒÃsø!‡’™sÚ?ïçëãYgºG}´ ŽJ,T‚K¾i‚Ôp.Œ0°`‘|Ñ‚åJ´eÒ¤I §y×âÙ˜?F¥†vs+¬gJ>ŠcŽ9¦ ,ðlöaJBTîyrmX§‚OË{õ…r&ÉUðûÞ³ÂvÊ2Ú{î„NÈöߟœ—gáPfÀ€uÚ¢E (ijQ,Z)UN@÷*¿€j¾4“ÀÁìŠxPŸ2eJYµá T ' î$B Iux(ãA š°Ü¥^lL+1¾H˜þªß—ø¤È"G²ûï¿?ÒOà ån‰8‹ip¸SŽc‡m8lÖ_ýØÀzû8l±J:ÜO=õTŸŽcŒ ™–´$-64˜$¦á–)Ià,º)®£sÃô4ý9•ãpç!;p0ÿ’CD=ˆ"ÿ,R*îoÑ0EJüw•E úr8øƒ‘œ5{¶ÙfËØ¶öÚkûd¨a=ÃRCgN9÷¦Ü?©ÃÝ"€|Û¸oKçR¸g`oÑ=þå‡.^2iwš¸o½õÖ‹íç«©ˆ@ó <øàƒñ{ƒï œ:2h.js¸s¾<ï…gÃðÜTß”çÄ4ypêpÇ1oQè߯i]|<üðÃ%1ÛÈ£øœ”îWß¼Eˆ—¬¯9šË%ldRRG4çD€Mc¬¹mÙo¿ýJ^“Rly^ 1¡ûiÈRûå×ÿýï/%)_¦¾eî7Ëïš iýäp¯Ÿ¶Š@uý:¯›Z-•"týp–k8¼Ãƒ–I¶Ä݃Ã=l#ê!Írî_ê<å?5"‡Ãƒ-ãiG€ MÌp S7Žxô䃇;çýh â¶á‘¾˜ÉÄq‚›Y²·°»ŸâÌÁ™Oý! ‡/º—üuîü_íú3Î8#® a*h(ÂýJ¢TÒ!Kx•™DOdyÒI'´)D‡Ó&®WúmÃv}DS`Í´‡» ‘ñìKûè(¡3ņŽ]ç‰TCd÷Þ{ï¸>8ÜÙ—S¤y7nœ¯›‡þR(Ƚ”:~2HÏ7=Fp¸æÛ$e¨7™öG®„í}úô «ütuÖñë‘/A:&o<¯´ÒJ¾ û—ãp§.¨¹þÅ^Öˆ¾Ç‘Îù~üñÇùCûe^°8.ÞÅ"YhÛqº‡Î:XÇ¿CŌȶ‡ŽP¦¡¤©M½‚ÃcY_ÌBç‘í óÍ,Ãy»ÿòåµ,"Ðx|gÏ8ãŒñ3ÆçQ&" • P­÷pî<4B¤9£ôx6ãY‹‘ˆÅžÙ¯˜Ã=Ô×Ô)Ï÷HŸqÓM7ùà40¥©õ6u¿¦p)v,žÍÃó£J›b•jKSŽîÃý€¼#ןç{ž«y7!`%<§§åKÍÓÃ=ƽvÖYge£FòNåÔQªî¸Þ;Üÿ›-Àî4™ˆ€ˆ€ˆ@õ0]n&éQöÉXCÜ'ÔWØ ‰•,z9]ç-‚ÝY´‹#‘ÉyHjNc÷Áøä™$QQ13Ç®;ì°Ã\ß¾}}òsÈ:‹Ì.(J‚O{8.XÇ‚éº#F8sÚ83‹Èð«-šºØæf­ëÚµ«[wÝu‹ÖÁùXt»³ò¸d¢öå—-Ú¤hbSÁy»í¶‹û•3C-{`v$C…=tû·ö@îÌÁå·±Ýä‡ÜàÁƒiC:ÓŒ‡€µ yup»ñÆÝž{î·1Ã:ŒäRùäçIöe~ŸÈ–ëwñÅÇm¾€ý#ñ–u®8î•ÆZ¥îX3ëq$ïµQÎ"€ê%çh‘ñÎF.ÔÙ®" M'`ù# >W}ÙôÊ´§ˆ€Ôž?økkã™Í‚Lü_[·…ãW‚ ‰f-ï”? Øq‡~x“N­miÒs;YP’[pÁý_nSY‹¼;ZÀNYû¨pýäp¯Ÿ¶Š€ˆ€ˆ@U0}=gº~ΤUÊnwºny[sÍ5E5çWÇe‹œŽóUáîíì×áTgÞäb™t&8øM^Å;fqd§f‘JébœÇQÊ_j‰á8¾E,;‹Æq–5Ý\ÑùbNÚp:(0œµÁ–M'<¬®3­o[Â%V˜N¹³aÄ~+ÎbÆêlh±³d­ÞO„EÂø‹ïý]t‘3ùž‡;lï¸ã_§EÅ#[bRg‘ûÎôñé­;ê3¹gCTEò»¹çž»d‡H¬¤ÈLàÕÜûÇ’N©ÝÅNž]t;+éüI¯aÉ‚ÚP蘢³Ë†Ü»ÝvÛÍüñ5q^íñ$,r36 §}2hI6Z׈pŒvÚÉÙÌ–<œêîÀäpïÀ_§." "P{LRÆG—¿ýöÛeŸœIÊÄ}òNl6Ôç”d;æsÎ9§3]vgZˆ¬òQÖLM&Åm»í¶Ì6h&cR§Œ%g­³.]aº‘näȑι7ß|3>HuM'%.M‹WlÞt˪+0Æošå%÷ÅMÄŠ Û-Y¦œ ŒÀùÍß‘G鯋éá{g» öQîDã#ò‡»éT:¦êèÈÁp¶%OGƒiP†âqÛà.¹ägr5>ÊHwŒûi‹-¶ð‘û¦Gé×5æQúXsïXÖg m¯o_m«-6”:Žâ0ÍRït·D̵u’íàlùFûÐËåáÒQVí ‰j‚ˆ€T‚N=õT÷È#ÄöÞvÛmÎò¹¿üå/nýõ×w8™y&îhFÐÉá¸×_=¾0â2ÌUzöÙg‡UšŠ@ÅÈá^1”ªHD@D@ÚžwÌ™ºwß}×-°À~¹1ÿp¼ õ„e¦qþâÅÂ482²™FÀûB%þå¥aØ×“-mÚ’®W¯^^6…8ü‘rYd‘E¼“—a°¼„ >¼èþ­½20¤Ý¦gXòåÇ’Á6ŠwÚ~œÓ'NôŽú†ähˆ~§s‚á°–ð´ÀñEHÄ „4Qî8é±n¸ÁOÓèv¿ÂþqÝpÒuÔQ^¶Æôõ÷÷"R:DÛóÒCäpÞYêÈO+qÿäëÔ²ÔGÀ’øÆÍÈ Õ×1 j¦lŒ„ ߇ì,9™²jðLãÝ.Lq|óÍ7Ž?Œç=žÁ-OFZ¤CÌ#wÉù—2FÞòÇ»#ye"PIr¸W’¦ê6&€óç(ÎÜÓO?ÝþùjzÝD&cH€s2½÷Þ{õÖ…D‰%ÜñeBDzò@¦áæ›ov8ÏË5¤JEå :Ô;Ûqö™Š$A©²å·%ÊÓ€¡eþé§Ÿú‚bÇiÊdt,É«¯nµÕVkP˱S§N®wïÞY‡çŸÞ;¿‚ƒ›JˆrOîÜ#÷Üs¿† (Öl¿ŽNòÁ)Ï‹ ÷áe—]æõšÑùoè^ •Wâþ ui* à^}öÙgc±þýû;$–d•'píµ×ÆJõ²á†Æe͈€ˆ€4žÀæ›oîr`taÞx®#ÈaÙe—Íoê˼ðÛŽde1ã¡K—.–O1&ZW9õ¯­ÜqT“ˆ€´kèG‡ˆÜvÝP5N ÀCcÐÁ½âŠ+Š>|«‡h]!ée1Cÿ;DËÛŽ36Xp¸IÉøÒK/…Íu¦“'Ovgu–ÿC²¤±Æ ¶ÕV[ù$žÅœí8¶Û‹¥#B¦bmC§\K£Æ'L˜Ð¨ÝC ’1©³IŠŠ Ãpq¼Ó^"ó7Ùd——…áx^x¡lϧù\àvß}w¿‰\AZ'_6¿ÜÒ÷OþxZîØB§c 0hР0«i UŨ—`tà‘§A&" "P>î*¾ËîÝ»ÇC„„²q…ÍÛ¯¥ïŸôøšïØu’vtÑi¾Ç:6™ÊŸ}š,•Ú%'SyƪQD ãà}–Q†éÝ(I™¤óhÜËD ¥ÈáÞRdU¯ˆ@Uø÷¿ÿí£2i,z×cÆŒ©Šv«‘"PœÏè9ò€‰Ãy†ïðÁq7œKD-ã$%úƒtö;óÌ3c™b3Ǽ;üðÃÝ”)Sâæ¯¾úÊ!’5‘˜iê©§ŽÛ‰˜'òü®»îòåÐ ÆOt¿oºé&¿j×]w ›5]j©¥|9ô€‰îImüøñ^¦ DÌ‹ÎÒ9DÿpÈâsü¦õ6w-s Íõ6ÚÈ_:,hçØ±c}' |›ò’týõ×;¤b`Œƒ›‘ éµâ¸Œæa4޾éàØi§ØTÇ•ÁFŒáà9÷Üs»ž={Ö)·îºëúuèµ8гL ‘H-}Œk–FȇkÀ îIÚËý¬%ïŸp ME€ßÿ4as¸÷E¦²~ùå—ø}OÍ$õ[n¹å*{ÕVUiÅo×É'ŸÿBÂìª:5VD@D@RöR)¨!–¥œÎÌœ5tV-w*æ€ô¼`Æß9çœÓrSÍ"ÐÊ,²Û„û›é¼óΛYÔffÉ î}‹lÏÌù]´…6TÕ—íÑ£‡ßŸzløfò5™ElgæLuí»ï¾™9ïëÔsöÙgguïËQÞF”d&’™c>îkNû‚}íÜo3‡pú “€ˆû›Ö»?7K”šÍ:ë¬~½u"dtP,Óµk×Ì$MÂî™9‡ã¶ÀÉœïq»i¢ûí[n¹e\Ç Ç ¼E’¬Ol¤/³ôÒK§«ý¼%fÌÌ1mß‘çañŒ¶s “r©³}+¬ó!ÖÅþÓN;mf£2‹bÏ,jdÃ6Ø>ôÐCõU—­¸âб‡rHɲÇ{l,ǹÁš{†û$\gÓÚÏxà‚:Þzë­¸6ñgNý‚2M¹¬ƒ ¶Çœøõ…sªú2¦eVÕ™Z‡€/cQ¹u¶iEíØ`ƒ âýÂýjGµsríèL¬s5ræ³n¼í¨uÕÙ“VËàj©Uy–¤»àžà¾°QUUy.jtû!°Ç{øûÊFW¶ŸF©%" …€×GU„»ý¢ËD@:.ûÆ/8ù¼†qÁF-ˆ@•@&„d˜}ûöuætõ­GFäñÇ÷r3¬èܹ³O®zÛm·¹™gž¹Þ3DÞƒ(vs>ûrÔMÑëD-ßxã^V†å¼í³Ï> x¢í‰À~ã7ÜÓO?íˆìžk®¹œuvù(êbûæëJ—‰BEÊfŽ9æpDNrn‡(ntŸ{î9‘?Ûl³ùÝHˆ˜æk@~g³Í6sloÍÏÿ.»ìYÎ?ÿü¹ëñï£Gö‘ùD…cœ[9¶ýöÛû‘ ö²é¯;Qû$T½ÿþû}9‘ühU2²kˆìP}–Fú–Š„gd‰È@b.´ÞaMT<Ç€mŸ>}܃>èÖYg‚Ã1²‚Q‹.ºhIÑ–º ¢…K€Ñ1|>‚!­ÅhYå ¤r2Œ„"1­¬éU³Î"ŸÇ$ȧ5½¶¶Ù“û@&" " µFàt/ÔÚIé|D #Àù†ë§C£;2“úÎý½÷Þs ,°@,‚ü2¨5H‡ ×Ažœ»è<âèlÌP~¥$ÌÜy磶8Úã“&MòÎvê(Gw‡7`†‘£9nQ÷ÍvvãlG>'5ŸiêM÷AB‡\ ©œI{¹Î8ÁéðN9s0Ω\§{8/äYЄÇ)ƒS‘zpð/¾øâ΢ÍC±z§Hº 6Ì;æ-¾Þ²a#Ç¢s‡û„óàšTJ'³%îŸÐnM;&QcâŽ;îp6ê¡cÂhÁ³æ¹”\¡Óư–5r_t²bGy¤;æ˜cš^Y퉴^çž{n”¦#oHÈ!ÒFÍÒa«œïs$s'R‡2hESì=ôÏJߊÄu(öG ßç˜:èÚ_kÕ"h:ôÀƒÎvÓkùßž8OMVå+ʘ39ß1XÆ. 5GBRþŠIdÛKD^q„›ÜJìð0 œ‚f£I‘\´©ÎvöÇ©N',M1¾#MþÆïJ‡Kcèà–Šn‰û§±ç¥rµI |Þ8;:ˆŠå)¨Í3oݳ‰œíYÉR›ÏŸÑDÁÒù°®¦äñ`ä#èB.˜jh·Ú(" " õý>:Ú&"PóH™Zˆ.M×i^D@*I™F àp¿ûî»äÔ^}õU/óúÝwß=ÝÔêó8ÈLcÝ;ý·ÞzëV?¾(-Mîñ`t,ÑA'«<´cƒ=H÷ÈÚ«“øšWadÒì³Ï^v½Æ¨*ê¡Ã˜‘etËD@D@D #(|ÃëHg®s# wÝ" ­Màÿø‡»óÎ;½¾9šöh¤ΰzd,A¨ûöÛoQï‡zhk7Ïw ¿Žüе×^ëàÆ<­Þ PZÀå—_^P»%J.XÖBe¼øâ‹î…^ˆ•m³Í6>ŸG\¡™F /y@øÂ©Œ\(Ç‹qjI³½,ùJò6jÔ(Ÿ?Å’…»bR”Œ C’øúŒ|,èóüñ^Æ,-KF 5¶ÐB ¹‘#G¦›ëê©§üï ÷O’! #ìÈýÑ%âê=!mè0äpï0—Z'*"PŒ@Þá®÷b”´ND ’6ÜpC‡“c×]wõ‰Eqnç ùŸK.¹¤ÁD¶ùý*±L´/–`´wÿý÷‹šŠ@Í c‹„ÑÁH ¼ÄK„EM+H M–Jµ’“i:\’O3ú(o8âùËÛ“O>éÈÅ‘w¸ÓÒ¯_¿|ñ‚e‘‡ß¬-¶Ø¢`[X ‡ #¶~øá°ª`J›ÂçŒNåÃ;Ì-½ôÒeJ-àTàêl¾å–[|’ñAƒÕÙ¦"ЖÈÁ“väò££I&Åè~)F¥vÖÉá^;×Rg""Ð!Z&ì* ÷@BSøƒ:È'/#©¬2p|l¼ñÆ>Ò}âĉ~? ‹.º¨×¡_{íµ+s &ÔÒ½{wwÀø@]»vuHɨ3² µK»'€ÄÉ?þÛ)ç]DQÑtÛ¯¿þúX'#«®ºj\ÖLyø}@Žg„ > zá6ÿË2î{íµWƒ8ü.$UÇø ¢ì /ìЃÇQOòkêGj‰ß­©§žºN=gœqF³$ê]ºtñ ºß}÷]?r‹ÄåÁHäÝX‡{>¿ u›„w~Ce"ÐÞèþC‰Íâ^¯f‡;a:¸èx[`âyuô™Jq©µû¥£ßùó—Ã=ODË" Š@xI '-§R ¡©ü@{I6ú¿ÕÆ:Ñ}úôñí錖Yf™¨!ßžÚ¥¶ˆ@¥ ¤šâhL—Šà­ôq;Z}cÇŽuŸ~úi<íÆyÍ”O‡óí·ßîw$’yìàƒv'Ÿ|²ŸoÌ?’Ÿãü~å•W¼£ÝöÔˆLÇùŽ®;£AþùϺe—]6-âçÓD§çŸ¾Ûm·Ý óì¿÷Þ{»Ñ£GûòqÚ!QÃȪ³Î:+ožyæqcÆŒq+­´R\§–%@ò‚t’"=%û/qÑÐr¸7†’ʈ€Ô,E¸×ì¥Õ‰‰€ˆ€ˆ@IÏ=÷œ—t ¶Ýv[%v 0*Â]’2ò6ÐI‹€ˆ€t $y¼ñÆã/¾øâ>ò6®ÐLÅŒ9Òë‡ •,5hû)ð={öt|šcD­#qƒ¤Í¤I“ ªBþÑGõ8ÍùëÕ«WA™†¨óË/¿ti$}Cûh»Ô:ÁÞ{ï=ß‘ÅHòüüñå¹ð¾øâ ?r嫯¾rsÌ1‡#‡jíÅ>ûì3ÿ½ÁòJ´–¶¯¿þºçßÎ;·Öá+~:<é@e$ä ÈË„5ö ßÿ½û׿þå~øá?"iöÙgo쮾m!’eÓM7£S¡£æ›ª,r*," 5F Í£÷»À:ȸù曽/¬&BWÖ2R9’iJ'¿e8—[+ήEp¶ãÄ#)êa‡æN:é$·çž{úü"8èc$T|ñÅ}¢Õ‹/¾Øë¸#U“JÀ|üñÇ®wïÞnÔ¨Q V‰ƒ'DÒ>ÿüónµÕVs¯¾újƒû©€´îm>OÜïŒâHÿ+kÈn¸áßéöcDÈu×]ç%™pTn°Án©¥–r|‡xàŽü õ‘èDÙãÀFj•UVqm´‘[a…üòæ›oî׿ÿ¢Õ+Ïí™ùä“6oS-6àµ×^‹œyƲd©µxšmzN¦[„y£Ûrâ‰'ÆýLâ'³HÄ¢ûšÓ)–3i :e̱—™³-3‡UfZïu¶³Â"t³­¶Ú*ÖcÉ‹–³Äª±Œ9Ò2sÊdæø‰ëÌ)”Y‚Ö¢ûj¥ä ì±ÇþÞ9ꨣò›*¾|ÁÄû4¼S†é&›lÒàñ–^zé’û‡zÒi—.]2“4)Z/ëÍÉÙèúlIzÌÙÚèýC»î½÷Þ‚z^~ùå¢uôèÑ#ëÓ§OÑmÔeÑÐÙ?þ˜Yè:eºvíZp ëP¨SÆ:% ʤ gžyffÎà:û„sÈO_zé¥t÷¬\¨°¹÷Kh”%$Ï,нÁó±–ÌF8„ÝüÔ:>Ü/å1õÔSgæ¼/¨#]à÷ç©tŸRó}ûöMw­Õùìü"Ü¡ è°ì¾àÜá^€C " " "PSn”F0"ýÅöÑ´q®ºêª‚‚’“)ÀQ‘…é§Ÿ>Ö3qâÄ8ŸÎnNG·Í6Û8ëñ›ˆ¶Ï>ûøèΰ̔D©ûï¿¿»é¦›ÒÕuæ©ýöÇ{ÌY‡Š—¿ÈB³?Í‘`\ù"u–͹ãˆf5»À¤’æ¬kT„| µBZÀ¦›nêïW"{‘'-W¢tß}÷­ó¤¹ÔcÎxׯ_¿‚Èm>wgŸ}vÑ3:ãŒ3ÜÃ?·ß­[7]Läs9 û‘7ëüòÑÒ|S ç–N‰†æü‰¦N¤ÉÛm·žNyŒ?Þ) fDB‡vñý€#Ö^{mשS§P´Î”hÿ…^¸Q¼9&ßiæÌ÷õp\"ÿ‰ò^~ùå‹ÖANŠÔ*Á…úš{¿PdzÏ>ëG Ƀ…{Å:7]÷îÝÝL3Íä×óïÖ[ou¬O¥tù~eäD±Ü$reDîáº0j‚óçÚä ={¢×ýõ׸‰Ñ[n¹¥MÏÛRìž‹;ÖÚL­v'è¼D £P„{yWþ¹çž+è‰5­Ñò*Piª!pøá‡üîßsÏ=UÓöjj¨½ØgDNÛ»³ÿcžu²Ê0gzdl9zôÑGgü™“;3¹…¸kÁ:ÌdcâzÓsÎLW=;æ˜c²Ýwß=3ÇYfN˜¸=\CsàdD§š´@<‰ë¯¿¾ û™\CfÄì„NÈÌÁ•™Ó&£m¡žÄý™!Jߤ.²i§6–19X&ÿ¬nÁ1~$¯ÉPdæèå4#)ÖŒpOË<Ÿ‘p¿7&Â}LB%îþ&o’™L›¢þù± QìÅÌ¥± åÍ ZPìÃ?ÌÌ™Ë䣸 Ûçá\ˆ¼ ·ÜrKZ¤`¾\B…åÞ/\SîÀÒ:7 ¾—©—2–¨:ƒs(G„Þ¬£4n§œIêd¦Á‹}÷Ýw™IïÄ2<ð@Üfžz꩸ÝrdðN‘Pæô£ ¸ÿ:€ù÷ò2.؉€ˆ@-H{z9/E¸×ÒÕÕ¹ˆ€ˆ€ˆÀÿØ žÆ kУ%bVVy÷ß¿OÞj&º]ÏXF妋-¶˜,4Ç·^$Š3M„N;ÚŘ9#É(: ÔqhùÞu×]þ/-ŸŸ7‡”#:•!& ã7£{œšðwÞygºª`dsú¬C7âR–Üçsl¯ŒÖ;Q»2¨%|ƈRG—;5¾G‰2æý•dŸÅ,M.|Î9ç¸)S¦¸e—]Öq޾9šÝÖÉåõËÑé&‚¾µÏ-ß?D¶#aéi§+:åX!{s¤ÇÈíp "õwÝuW?ª†Q;šèíÑL¾Å'H¥mhôó{›æË`=£¬ÓÓ'›Þk¯½XåÎ;ï<Lj¦tÄßðû? HpÇw\ºÊ'`%R=\î;"ßSKï9F :Ô—áž#)=#±Ðã'YýC=äöÛo¿t÷šž—¤LM_^œˆ@CxhO­ÔPZFó" " " ÕGÀôN $/vÙeÿRZ}gÒþ[œ&K¥µ$Õ“µ K/½Ô;áJI/X$¦ÃáBRS$”0œK¦¹ìeŠµÊ¢Þý5#™`0:L^H¥pØ!=€d8õ‹Iùpþ …ƒ(5d!ò’éö mžm!áb±Ä†é¾šj$€c<ïlç<ø\™— ’??’€œŸ–ÛÁ'å3Æçéäh,úÙþ[»#'6ß©³=•^æ»/òZOX—N<òH/µÂwf{u¸ÛȼØd:,óÎö¸Ñfè\EvCÊ%ÈŠù¹¡#5·ÚÍ<óÌqU±ûŽû‡z0’¯ò›OB[îYäeXæÞ=ýôÓ£LX(_ËÓ⿈µ|Æ:7H(Â=¡Y¨a¼¦¶Ã;¤‹š¯"¦Ó(kœ¶6̼Bµ«š<¢‰d5)ïT·$¥~4Î"" qv3"]‰8$¢Ñd%üˆ"O‰45é‡ösC†åî»ïŽÅ¾üòK÷ôÓO»?þØ™¨ÅÙB”#šÉÅ ‡;¥ŒvšŒA©ÍZ/" ìöÛo÷ŽvKˆšlq^ûÑGuüÑQÇŸÉI”iéKøêèlMãû-ǯÏpNÓÙžíí·ßŽÍ+å$&äžxóÍ7ý*œî-Ñ‘@';òÜShº#¸‘còÇHž»ø½Jø¡l-Nåp¯Å«ªsh4|„{k÷ò7º¡*(" " "Ðd$\dv°u×]7F}…ušV†À¨Q£Ü?xùR_a]™#¨–bˆr¬/Ò±Ø>¬[tÑEý_©í嬟m¶Ù|Ä{9û¨¬ˆ@e l¸á†>‚œÎ¯^xÁÿ!EÄ{HZL§S¦™î“[V¶í«¶4h±¤Ÿí«µ ·&uVOž<¹ÁH,eÖUbJ½HÖqÄîñÇ÷¿tt´û­·ÞŠNøk®¹ÆoC*,ŒÖ¨ÄñÛk’”i¯WFíhùwIÊ´ vDD@D@Z•ÑWiÔ•%lÕãw¤ƒ¥r2–ÈÎõëׯ#¾ÎUD@Ú„ïµDwëÖÍ2Ye•U3]t‘ׄÇÉΘ­¶Ú*¶ïŒ3Έó ÍäÕ*_éí–|³IU¦Qàõå˜H+/&’nOç[›K¥ON Krš6§`ž\O>ù¤_gI­ý¨§‚X¸ï¾ûÜJ+­äósüíoóÒeH÷ •OG<0 Câ‡}:‚ÉáÞ®²ÎQD $ü¤"ÜK¢Ò¨Z©Û e]µ'ÔNŽ3‡¤hÁÐuM£ñÂzME@D@*Kmt~>öØcnàÀ9K‘ÐrO;œCÄ{ØžŸ')ëó ŒY÷ÓO?ù(ùm¶Ù¦NBd¶7׈œïç/¿ü²ûæ›o ªDÒŠŽÚQÊÒÄÊD`ãÎû¾Œ†C¿}–Yf)ඇi[ráúá<ÇÚzë­‹&ž&6 OýõW_–rœW¥ ‰˜gŸ}ÖËšÁžhD3Ÿ\hºkè¾ åª}ú»Ñ 3Vû©ý"ÐÁ ¼òÊ+n©¥–ò ‰B6î¦"á‡ëòË/wŸ[o¸ý*5µšv½ßû|à®ü=9 ÝÆ~”³¡µ2Ú ðáG¹Ë-Â=ØJöÒ·qÏžaQÓ xÈ^þ'XD]°mí‘… ‹µ7µä Ús£òÔÞ¥ÕU7!C†¸ /¼Ði{ôÑG·ØÉ ›AþœÑ!gp 㥓7ÀÛn»­#Ùe°=÷ÜÓá}÷Ýwý*”ä_ í}úôñë&L˜àå:p°b8¡×Zk-wÒI'¹ÕV[ͯ9r¤¯Û/Ø?޽Áxù4’“c§<¢Á):`Àï¤ûä§´•z1œõ}ûöõ~†/¾øÂ·—Heò5C¾„$š×]w;÷ÜszãD`ûí·¾ÉgœqÆ!„x@IDATPÜŸ =öØxqãï3hŽ#M‚-»ì²^w?‘ÛDP§Þxâ‰.$}ÆåÙ¿Ÿ³"”¥N¸‘#‚ütÃÿ™gžq8Ý1~Òîb#à›Ê¥÷ mcdBšû‚Ž ¤„È—ñõ×_{8Ò.ÁãÜ‚~>÷ëî»ïîùM™2Å›cŽ9£…#6ÜHºJÅçŸËйBâÝ`‹-¶˜{ýõ×âoºñäïà3ñ‘=ƒÑ1œì\#’·Öxn—)vž¢LD †ØžñÌ~T›}V–ØÂ×E}úݺtèÐ= {@÷€îÝuïsb5û¹[ˆ€TŽ€9 ýû«9­+Wi‘šöÞ{ï²ß“Í›™“Ú×fŽÇ’û›s9ÑïEËm·Ýv±Œ9X‹–)õ½ÐB e渎û›1 øÌҪל¬™9X}5æLmÔ>¡mæÀ.vx¿ÎF¨eæ¼,«>s Ôg1™9ØËªÃ:3 êHšÊ¥¹÷KÚ†ƒ>¸Q\¬$3=ÿt×Ì: J²=z´/kÒ/%ËX>€XŸuê”,®o:µÎ‚¸o ÏøD6JšjW^&"Pœ@è‰^Èz³70-¸Z´ÉÖ;~[¢!¶ñÚk»ÎÖÓ-¨~¿ZtÕÕ£G»_~×8Õ¢À¶îÕ«úO¬žÁÇwëøñ±eËY¤Ýê+¬—kqæ‹JýÚ" C4k-ž£ÎID 4"Ÿxâ á¢ÆK—v.D¸‡(o¢|‰¾&8¼{³?ÑÁDÛe—]Ü›o¾é&MšäW%Ld8Ò1Áˆ*Fºƒ¨oê%’=ȉ„2LguVwÀxé”ÐŽt{:Ï1Ð=ßo¿ýÑÙy#ŠžHr8 ;Dƒï»ï¾îôÓOwï¼óN~—‚åbçQPÀAÄy0 ¯­¾À øcŸvÚi>’žúˆ‚ßi§ ª!ŸÈ¸qãÜ%—\âÎ:ë,u]Pà÷"»I&ˉõ×_¿X¿®©\š{¿¤ :å”SÜÆoìGB ÕnÎët³¿Î°c„G>Y*ë¹O¸ŸÂ~0bt#'°¥—^ÚËÕŒ;6Þ›HÒÀgá…ŽÇÚm·Ý* Dº3ˆöbÖ£GwÌ1Ç”ÉPlŸj_'I™j¿‚j¿äTRR†axüØô³/òÏ>+w¤ÚXœðÔSnÝíwˆ'sçðánãîkÇe͈€ˆ€ˆ€T/«Gq; Oà\ÓoÝsûqY3•#0øðÃÝe7Ý+|ñŽÛÝ2ö^˶t¯Þî•7Þp>ø ëÞ½{-ŸªÎMªŠ@kIÊ´g(HÈXd³O Šì R/8ò‘AF¥Ã)‹N7t’¯vîÜÙ!ËbQò^~¤œºšZö—_~ñ²(Èâ B§ŽoœìåõàlFþ†?:Jæšk.ÿ‡Ó˜N‘ÆZ{àB[‘Äyî¹çÜ&—‹Îßÿþwß´Þ{>Í-äjúôÓOý}Æ=‡ƒI¢d^RFîèŠëTE@ê=ºaK™¿×a7ME@D@D@Ú!ËGýÏŒ.nÿÞŠno‰ËôÓÏ?»›î¾'VÝÅô‡kÝÙOV3" "Ð Ì6Ûl>â½Mé½ÒJ+ù¿JÔ×”:B4}ÐfoJìC=]ºtñM­#ì׸Ð:@økkƒG×®]ý_[·¥=ªöеAD@ÚŠ@~è_×VíÑqE@D@D@*Càí÷?p>;1VÖ·çn6-«<[Ç[â¼ï¿ïø{¢¿¸B3" " " "ÐÈáÞ.¶NUD .œÔYÙCÒêÖ¨5" " " íÀðo,hÆ -·,XÖBå\=fL¬lê©§vý7é—5#" " " €îíŠë|E@ ü–ýV°<ÕôµXD " " "P…Á6âöÛbËçsN·Îª«ÆeÍTŽÀ'–,uücÅ {uïîþjIùd" " " "ÐQ ȳÔQ¯¼Î[DÀ†»n¨=wM˜à&úY<±]¶êç$qTtæÚ[oóIçB¥’“ $4è¨äpï¨W^ç-"à äîz×!" " ÕOàò›GœÄ›o^°¬…ʸæÖ[ce³Ì4“ëÕ}í¸¬èˆþØOZç," Ào¿eaÖOɬ-¨^Ÿõ•»û¡‡â lЭ›[`Þyã²f*G`âË/»¾ñF¬p[ÓnŸfšiâ²fª‡ÀÓáíµ×|ƒç4 ¦VOã+ÜÒ/¾øÂ?Þ½÷Þ{ujÞqÇÝ\sÍUg½Vˆ€ˆ€ˆ@J@÷”†æE@::îÒpïp÷€NXD@D ¶\yË-î×_'5¨Ÿ’¥FžI“¥Rµäd* ¸«1b„=z´?âŠ+®Ø¡î={ötÏ=÷\Qúë®»®îEÉh¥ˆ€ˆ@J@’2) Í‹€t8ù¤©Špïp·€NXD@D Æ\3æ'3ÿå/nsÉ*OàÿþïÿÜ wÞ+^|¡]×e–‰Ëšj%ðÇ?*.±Z¯Ú-" í…€~IÚË•P;D@Ú„@>Â]Š2mrtP¨Ç&Nt¯¼ùf¬ íöi;uŠËš©d{¾øúëXáÀ-¶ˆóš©>DnÏdüØ ,à§õß½÷Þëî¿ÿþ()sÙe—¹ýë_‡Î[D@D  äpo4í""P;~ûí·‚“QÒÔZª"O–ºã}ªªýÕÔØTN†‚Ûm²i55_mÍ2dHnMÇ]œyæ™]ß¾}#œïr¸Gšh9ÜIED@j—@V˜3ÕIR¦v¯µÎLD@D ¶ ü0eŠe‘©Á7é²ä’aQÓ øÒ"Ûïšð¿Ä´ë¯¾º›gÎ9*xUU’|¾ýöÛî+K<Çs¸EYÄÍ0à åTQ‘²¿üò‹{å•WÜ;ï¼ã:wîì[l1÷ç?ÿ¹Iu3 •¤¥oXRÞyæ™Ç-ºè¢®©—üѽi£]>ûì3|Ú3ÿüó+©o“®†v(—€îåSyš"—”™JISkêúêdD@D@:‘wÞé¾ÿá‡xƒ¶T²Ô£Â3#M» ÷`IH´Þô?ÿù»úê«ÝñÇïÜé‘§vZGâÏÉ“'»…ZÈ92Ýç/¹äwÑE¹÷ß?® 3&LpË.»lX¬wÊþ»ï¾»7n\AÂâ馛ίÿé§ŸbnYÒ:Â?üp߯n¸Áyæ™Þ9ÎA–_~y7hÐ 7tèP÷ÑGÅãN3Í4n¯½öòçËù3ÊŸ{î¹nüøñnÒ¤Im¡üŒ3Îèˆä?蠃ܬ³ÎZ¬ ­Šý"U‰ˆ@µPÒÔj½rj·ˆ€ˆ€Håd:™snë^ÐRŤr23Xóæë¯_±ºUQÈ$ïÑ£‡{øá‡‹ÆÁ}Ûm·ùmÏ<óŒ;ì°ÃÜÒK/]§ìGá>ÿüó:ëYñ믿]Ÿ_yýõ×»ÝvÛÍýûßÿÎorD™ãLÏÛ£>ê×Ó)p '¸—_~9yðÁyãœÏ8ã ÷À¸'Ÿ|²h¤:ÎôpÞùýY¦'Ÿ|²¯v”ß¡&­«2SltŸ>G!˜¬k×®n}}/WäJò]øØc¹§Ÿ~ÚÑщ1b¦ÿþ©¿R•ÐαcǺå–[ÎÍ7ß|•ªVõ4ƒ€îÍ€§][—_wZäÒ?ÿùÏ‚0ÀGA¬Ô‚4’@x( ŧšêaVS*!ðºIX<õ⋱µý6ÚÈÍü—¿ÄeÍTŽÀ¿Þ|ËML¤[ëé-’YÖzp<§Îvd[ºtéâ%SÞ}÷]‡“=€ÄK1‡û°aÃÜyç#Üó¹:#ÞËvÞyg÷óÏ?û¢D´¯±ÆÞ…æ9Žñ|äKZj©¥|ä;;í»ï¾>jv§F9"á—Xb _Ï|à7?ÿüóîì³Ïv|pZÜÏîSO=µûûßÿîemˆfÿðÃ=3f8Î8ï<°NZ!ÕFà€p_|qA³÷Þ{o9Ü ˆ4}¶Œ®I$ÓíÍá~ÖYgùïEdÅ>ùä“´¹šo#r¸·x¶<<$öêիεðP'¦È¿Hý©$µŸˆ€ˆ€´á7ÞXpðAý$'S¤‚ W]PÛŽ}”˜¶H+,<òÈ#ñ(矾0ÇÁ in£¿V ,°@ØT0ÝÿýÁ6ÜpCwo’!¬/6Åq½Ã;DgûJ+­äˆv_xá…cq"ÙqJáìvÙe—¹vÚ),úÀ)"qÓ6"ƒCÛSI› .¸Àí¹çž~¿»îº«¨Ãýè£öïŒt@ÐdhRÃÉìñÇO7i^ª–@úÙ¯Ú“hÇ ¯¾¡öÓO?õ¹/Èå!k[r¸·-½‘Hv“wŒ6rWz (ij½x´QD@D@Ú=†Q_wû± Ì;¯[Ëœ²Êàyüº;nú[×ã²fZ‡À\sÍtÎ9ç8$%pNŽ”IFqlá½òÊ+n4q†ä¨Ï=÷œß{¶Ùfó‘åi»ØÐ­[7w£u†­µÖZQŠ¡¡Ã‰ãhî¹ç.(ºãŽ;zg9÷à믿^°-]Àñü Ñ÷$’%p‹d«sÎ9§›}öÙcÑbºõq£fD Šœ~úén³Í6s×\s1bDµ¼:š:xð`Ÿ[‚Ñ3§œrJ»m4^ÁÒù°NÓÖ' ‡{ë3×›@`•UVq‡rˆ»õÖ[Ãe"P)y w%M­YÕ#M'0Ö^´¿ûþ{_Át–m¶ÙÞícë~äÙg}3gif·þ«Ç&ß÷Øãî«o¿ñËkš¦æ\û[ÜÖžgªµÝ황ÚÖ2n¿ÿ÷é_ÄÊwÝj+§kGEgÆ›ŽíäO?‹uîØgs±Ž4Zoæÿø‡»öÚk½lÌo¼QíMbP4|‘<8ôÐC[,9è /¼O‰Ï¼³=l\}õÕýˆä}Ö—šÒaw¶Sv†fpDšâp/åL ‰d9æ˜(“Sê8Z/µB€$ÂätøÂ~åp¯üUå{gµÕVsŒœi/÷7ß|Ól¢M½{÷ö.陈0Á_3Ri¯í·ß¾Ž,NZ^ó-C@÷–áªZ[€À‰'žèøchâšk®ÙGP•‘@^Ã]/èñ.Ð9·'}ò©ë=ø£š^¹çn·¸ /oÏöÜ?_qÛ컟o⊖˜.u¸bZ»AïøŽK.q½ÚÃç䇿ë;Î6óÌŽ(Õ¼µÇvçÛ¨e€Àå£nŽ ø°Ù¦qY3•%&K¥æí7Û¼²Pm"@Ôúí·ßîí“&M*؇Ġ¼/ñw饗ú?¤9+mi”ùâ‹/^oõlo¬Ã½ÞŠêÙˆ³}ÓM7uwß}w=¥´I:#¿úê«^bi™e–qÓO?}³!àÐ}饗 šÉÐPt’á$F¹#éèüóÏ_Gö©1 ãýý½÷Þó²)ŒæYtÑEý(–ÆìÊ„:ø£3zhS¹ÆyÑ鉌Ëb‹-æÏ©Ü:šRžL÷߿ߕßW§ZŒ*úá‡bÕüNì¾ûîesŠh¦I¦jÒ^ÚID@j„@^ªHISkäÂê4ª–À56’)ÿ¹¼â–[ªö|Úkï½õ6·Ò}ýß‘çœÛ^›©v‰@ƒ>1G¸G‹å6^{m7_"·7h¦Ùytëøûb=k®ÔÕ-8ÿ|qY3­K½õ-Q0Ñ‹$õÃáBPÒ)Á>þøcý8jÔ¨°ªbÓ\0Ö…#­>kh{}û6v PSg;:ðC† q'Ÿ|²ôG’FA[¥©rÕN€¤ÉHÌ 3õ7 ôÀ»êª«:FÀ@'m){衇Ü:ë¬ãŠ"ápøC¢ ‡ù{ìáfši&·âŠ+úÑ+8«É%‘7rI :Ô­°Â î/–ÄÙ+ò5ð‡¾8O¨|õÕWù]ýò 7Üà%±ÂñµsÝu×yÙ,ä²6Ø`Ÿ€™(o>ÿ8ÿ²·ÞzË'ÒêèÙ³§?rHðA{ønMóL«“Ñ䔀-#Šh {˜~øá.ÔW¬Žæ¬£sù­`¼;¥çŸ:Û Dà|Ö’µ.o]Þ-v4têè]㋯5ì»ï¾óCjšš@‚LöÔñ׿þµEšKýüñEZ)£½|q¶ãJµ[õÔO@îõóÑVhmWq \3æVw¢%u«ÖÅ-7ìéV²ˆ"¬ó<…š´­Í·œãUk»Ë9G•­~WŒº¥@zç-•,µ¥®êM9ü“=_S²Ô@¢u§8VèõÉoºé&‡ô&©¡O~ÐA9¶cgØH«-+üÙÀÉŒd©¥äkž!Ú¾% ç"ŸÁ”9ì°Ã¼MXÇ.M‰bMëмT:á.ºè¢:Måûƒüh¾#3BÖ©áÝtÓùžE¾ðwÙe—FJÔW_±mðà‡$Çá˜ôT2ĉÞW¾ðg™e–b»]÷‰ µ?÷ÜsçM|ð/Ç—&Œ7Ùdÿ#ÅP¨æ½wÜq‡æµÅ[øžßæÔ§}Ë#ÿ N5•þ”GP¥E r~ú÷–½ c+-»Œ›zª©Ý“öü™=Øßeþ›YDL5ÚPK¶TV­í®FÖjsÓ ¤'³Ùs^¯îk7½2íY/«­ó3Ø´ö°¥EÊZŸÀóÏ?ïõÛ92ŽwdcòNdÞO î¼×TÚpæ ƒTïv8vˆ>MµÜyÝÊr*àkICÒáûßs¿ ‹ó? ãyŸ\`È“ÊD #Hßqù~èÒ¥‹ÿNœ8ÑËŸÀ€<øtøI ¿ ‘àø­Ž †¯*5"Öéì 9öò#YðÇ;ŸG"Ê‘]á3úá‡z';>›§Ÿ~Úû™ˆROmß}÷uǼ{×|]©ñ¾Nä>NdìÁOÃwãÙgŸ]Ó"ì7ḟ=~(Úÿ‰öç}n”ã{¥”Ã=ÔÃy-µÔR^ÖÙ¬ ™ÃyãŸÂÏÕR†ŒþF|qt6äî|'yä‘-uxÕÛr¸7RCEø2êjIÐÒ/µ°P†¤ãaç/_LÅŒ88Üy` Ã:QÕyãƒÁø/¦ü6¾lø»óÎ;½3ùÖ[o/V°LûòÚ¡_ˆ$ˆØo¿ýürzŽ YáK…¡yDPœzê©a·‚)e—ÿ¢¤½‹t<ðGo" wèQ-'2Žøóã‘7ŽÉXtN >;í´Ó‘÷içåù²áK‡/‚† †ô²2äÈt˜b}ûÞwß}õmŽÛˆä'ƒó®»îê ÂN,`30ãጀÆÑ^x¡À|â‰'Ê–›aøeøB ÇãcذaE¯W(£iåä?»Òp¯[Õ$åø·½${¯ß'ëÖöàÏ÷ô~ÆoÈ=ö{ô±éFÎU$áè]Np›X‡5¶ý曹³,²msÓ—|ôÙÿ9‡Ø6ÿÜs»Ó†ìúm´‹†žyHlúÖý÷¹‡ž~Æím/#ß' ‡ˆèÜhíµÜ¥¶~VK4ÚXKëöIS×é^°+χžy¦CƒN‚Ôæ1}ƃ,Òèý·q¦™&Ýäço¸ó.w† mO t¶‰}vÜÁíkÁVµ‘kO¿X8v„Eáð‡k=·àçj7ߟg]y¥;õÒËü(¿Óïÿà3d»íÜCöˆŽ‰°ýêÑcÜNö;‡í¿óNî8‹`ÚiØ!î6û=Ç鬋9uÎ?êH·šEeåíª[F»Óì%ñ_ô7Ê»ÏÞn=½`äÑÔÜ2q©Iâ$¥QÙùt$5ïh²¶!:Ÿhï#[•2TgÚoLj8gô©Ï‰H G¥¼÷c´3¥p€oÇ×_£êC™ú¦tðî“¿‰8ÃqÖ‡;ÁP©, ¿?t(°/Ž(œÿçœsŽê§®Е֛Ÿ'r—@6Ø I ':p†åßÒ}ó\‘Žã-Ï8ÝGó"ÐølâûHÏ Ÿ-äwƒ| #?R‡{Z>?߿Ă ý3»x– ò$ˆ”ï(:¾@ÁÏE‡¹&pCî©!C³ÿÔÜö<ŸÁ8îy_H;$C™#FÄ4” ðÁ!5>aõÈ#xŸÛê“a!Zÿ±Çóší¡X •îÅäo(‹¯«TÀk¨+¢¿_Êá~ÕUWÅÎU:XàÃwt¨ÐKô~1ßWz Í· ©Z¦ÚŽU+½vô\嵯ŠQÀ™‹¼¡nzÉøp0ܦ!c8 ½V<0T(ÿaâÃFO}1£Í|ᦽ{i9zåòÎv¾\éñ$áFj|i§†óœaGyg;è´—DùFöçK˜mym°´îúæ‘3×eÞyç­S”!S8ÉóF;ù)æl‡1‰CˆÉ÷ÌR)ùÝ|ýÅ–Ó¤Fa;‘)Å:GÂvM+K æ??•=šj(EàF´-²[gÕUÜœö]8‡=ˆ3ñÐHBÕ†Œïu¶ß¡Ž³ýˆßzŸ}݉>c±ú.²Nê-iRêl§ÚÅcÆw]Í9?)'!W¬žÆ¬ûÐF¾u³ßž3.¿¢Ž³ý?úôS·¯u^o·ÿu~OϱÈÂmmxl1g;û¾gÑ3ûŸx’;È"ï*mÈü¬kœ<ù”:ÎvŽõ•9jŽ3GȪý¶rïZz)ûaÊ®÷à8´¡Sg;å‰ôßpçAF©}îyþús¶Sî ‹ìÜx×ÁŽŽYí “nŒE÷££ei‹V•UžÎÇ·ÿ·SŽÚéø\¯D´]å®óprodÇÈÆ¤Qèùr¼Gâø!Ú=Ÿ7‹mÈm¦¯üþé2ï&èÄó›7"K‰ DZ–7öÛvÛm]ß¾}ó›ü2‘é¼åsd!ó€<!Š”u<¯?0éPÞyç}Ä}Þê ˜+ùÊ{#:Ö8Û1Ú,kžW¨#?ÅÁÆõ(Æ%_VË"К½‘w¶§Ç§Ã -†„nþ8-扖'`08ÛÃúRSžÝ‘æsŽ£‰”POàsÏwC9†|r±Ï7í þ“búìtàÃw¶‡mL eÄß 9ÜIš·´}ÅÔòå›»œŸ(K›Qu~6¤‘óþ¼æSû7ž€îgU²$X$KèÑO“µÛ‡¢Íq&ãXÞ}÷Ý‹óëÐ#'â:ý¥…y  wŒHxz×Zƒ–<Î_œÃ©1dˆÞºbƃZHÑÐCVÊx à‹‰ñLq¨ÓÓȃ ÑãÁèY¤Ã 5ôüFY2<¢h/ºV0€E~è=œéTZO©yþGĽ‰DzpNuûpb\î³A7Ê´?|äa÷Ú¸{}Ôxxù8ëʫܷ¿K¤>t¨»ý’‹Ý@ˬ»9QXÇ_ïuº‡ÕõNqä?dš›¬N³zŸ³g¡—ï¾Ëwän–ß“¸?gMƒ?¢d]Ãmø.õnŽX?m²B{í°½›î÷$Sÿ¶ÿÏÞ™À[5µ|½xÍ^Ñ+•©Ê”1Ñ@”æ¹4Ï)SèM©2”(ED“æyNM¢DÑ@ˆR"”¡ø¿xÿë»´vëì»Ïpï=çÞsÏ}žÏgß=­½öÚ¿sÏ>{ÿžçù=÷ÖpÊ Ô/Öéßó:2žÏyî #´ŒNq³‹É.Q¢m2Ï™LÒõo¬“Ž+h×0˜ÐË™W—\£^óîõÅ®£Ž³VuëF•{J®+H­Ñpo'ª‰Þ‘,e}ôèѦ !ï[¼ò~‡–¹?HÊ¢Œ'ïš8T¢MDƒ£•Œ|JA†£ÃÌX‘An‚÷EÞÅÐuwƒ»¬ ýðÜ Çû¡;$[ÝwIÞ™ˆÒ´m yÇC.Á5êœñŽÇ;ïÒDÆóÎIý1HE¤_lÌyo?~¼Û…‰”GNÆmnB™QHw1A ™@*’DheY¢©Ø~ŠŒÕønÐ9”X¢×cí7#í¸wX#@5’!}ƒö:÷ŽD÷RÈøX§HŽ ”4%[§#–Gg™.^¼ØÉ’‘ä:uMÒo0Ço–­Aâ=_@ª{P_–Aã'úB­ÜðyðÃÐNGÇËì< ÚÔÛ'‘ðDSø¢©è•1Q(ÈÕ[Ç‹L„¼¿Â¶¿»ÎçËC)ççA>#y\íq2~o>Ÿ‰˜ d-[w|æiµ#›‚6°5–»öë¯þO¨h5¿©£èÊEy‰¨[¥²š¡”-ጞx­A‰ŒÉ„¹Gi>¤SÏki)´ 'ÛViÂûZM X+®£íÊi¢¡L“¦†ä#úšh÷úUŽÕ¶uN‘XŠÁb§è(£µÓ§©óôï†üÍMúž± Û‚MÑ¿‹½nïl–k‚Ùm'iýY$o¬õîÚE½·åc3Fîsom|OU×Å$-vÛ>ûÜ65zë5}ŽzogÀŸÁdýœáXþÊ+ªô•Wx-Ѻ¯xý «&†è];®g/]ˆ$Ž +cC'à~ªÆã‰Gš>?ܶÝë›èu ‘~ŽêÞ¡½·Ï¹ˆ~^©ÔªµúS_ó!]se“B@šF,õpådpÐIE¥ÞUgÏùådîKØ!â=Œ@,ÞA]ã7ˆ 1káH{»?³s¢v >ó e¶_9^ÈI@¢G3—¯‰ÆKEëË¿ŸìÖÈ"A~^‰€Q4Ì‘˜ j‹Çܽ¶¬ˆ:6f›Y­],ûqšIŠÁSYî+–~¤Mbf)1¸&¼W¢©ƒÈv{b¤b¸Á¹UâÑÍòkÎÙö‘æ<¬@fCˆÇbÜÄÜHäXˆ<"ÛÝþH+²E3ìv"b1":‚Èv{,$6éLûÖ¸¹£'†AÈóÒoh „-iª«#| ô­ñ£AôDzÎ)d{z‘Ë|{ÓÒ‰M CV,AÀ-– )|º#Y–Gk*Þª‘ÖFëȽh6@§ªZ²Ý¶Å™6P§±ÚˆqHÜ Ú©d õ K¶Û6%õÃkûF íª=s†·œ‘…¦LöëÞ¾G¶{õƒš`'z'Â…œïÉÜ”½úÁOÿ²±cBÈv{üÏ¿}Ù:ðóOvs¦ç£´Tµ–úwÐ%Ûíö‹/,ªº6?š*ìcÛ0'¾K³ÛÜMfùæÒ×{Û(ŠjÍ:$XÇ3rÊÔ4h$Eö½½Níg½™„l·È¥ÖücÁêJ)5ÕÑ]6%µ®4û¯æ°v\Í8°Âh¸7âTbA€ŒètÁEÎH2 ±ô)mA :Ô<ˆ$ÑK&üF€'βxY†®òò5’UÃý€@ÇqZYmõ¬°ë¯?úLDNÁuFí—m‚@,á JIÖ†4 ”F3îúNÊ8„2Ärz -"°cÕ÷£onÜ®G}{*ÖÇbÈ©¸ƤèE³KtäZ,Q8Üt~ìŒÒ3é7ÚÄuù vW#,ÚØeö#à:ZMÍþÏDF» «ÉFs宜ŒEâ¶š5좚±x‰B»9œÒÙZáÈ tá‘O±ö©NDz̮Äͧ;ƒê3hÛ6í¤µT”}yµ¾ì,ý‚2[˪0ªê„ö-º®S1õ†íÑzï«ôïæ :=¿f§NŠÈòD˜«î~6þs¹X¹Ç¸í.½èÂ4ÎöÈw–׌hukDµ—,Qܮ٘eÊ-ûáã'ÖŸ¹ä5–…”A)"×\G˜»]–3rVn=‹Öõêf¾Sé!å@šéQd,ÈNf"Â’¬ltŸ­\”Yf÷Ë\âƒd;ûô³¡ßljÔ¨‘§,€fy< 2ÿ—#Ïê<"måÂðþMÖK¸Úñ }Q?ÐZß¾}MÑT?`÷Û{õÃ2ƒÀq™9XŽÍxx‰Õ(òéV~‡`ö§ùEê‹â®Î^¤¶vߎ;좙§GÓŽtt³lui~ ¢cŒÕüš]hÿ±y¼ÌÕ‹WŸÒOâð3ò8¬¥gA $U(ÀiíåiÓÕø9síª™S¬ÔšÍXíи±Ý2¿@GéD²¢ºÈœµa÷ Π߇{üN‹d›ûaûŽeîö]pA,‡„´ù^׎Aêaæ’¥ê#^nbHé ƒ+®‹‡¿;ä]¬QÀ•Ͻw×N=ùo‚»åp÷b¶/Ôîu»tUèÃcßi¢gêÂEfb'EÇÆTwý¢tf€”mÄr.LLY°Ð»€ õw§L:ž½e!&^™}T „LL² Ä?Ó´ŒèÕHé&µª"!$ûŒ!°zõjEùÆC:xM§`)µöàXøý$Ò|Ñ¢E^¦?õžÖ²„ÖPD@Š5€ßt†“«@ád÷™Rš¾Ÿyæ{¸™sN ™BºC`SOµ‚8á£(²L ×Z¤Ðk-]ÇÉŽ‡‚ÌŒÕmP ö^½zõÌfjëA¢smr5œë‰'žP7)îÆy-sÈ} …‚JÉ´a?×"ã!#‡@LÆŒMŸ>]!3L@(ä;xZ#ƒ ítë°èß¿¿©IaÛPÓiånݺJÛv2O]„pÏŸ­?;Ò% ãZz"ÕÝãÒ³ìÞ„9.œ÷0\ŸVæ…ýá^¸Ýcc­’Í1®~ë¿!qø±ˆ—qcË98JBfÐn†Eι © sðB]¦`£Ç„#Ü£{ÜqÇzMþ«£ëÓkÇébuÖ Û3J¸sºôÖN;¹nףͿڳGÝØ¬¹bníb­Y¼HaUBëÍ£ÉÞgÈPñn÷'bé7Þ¿/–ßôXƈVû†9³Õ’7ÞÐÅoW¨•:ª»“-€#âÉ‘/©¹:[í--ig ¸ÆÒ·´I~æèÞýú3¶Ö1ŒóÍî—yÆ 8óò#õ è…¢Ê8´Ä?H"w ™ç[dNÍ›7wwɲ Ä !C†¨•+Wö†Üï”)S÷!ñDQcWvYâpít~{épÿþý¦Ps—.] ±oO‚söY]+ÉFˆSãŽ)’‘ñJd<ä=„?ÏÉnà(ÇBªCºCl[Ââ¦ßÖxöÄÁ±–pçù Š·Ú‡V6Ø4§VÜä‰]ƒ³"h“z†î8& `ÆmÛ؇ƒ‡@Pm@ÛNæ©‹€î9ð³%‚msŠ~F2nX®vm]½ñHÇffÞS×ðÞwß}Ë\žEkþ¾ìvwîw*¸ûüËþ¶6ââ=èÌ[»ñÆœŒ]OÏ<=÷ééWÚ&‰pO ®Ò«  ß|÷zU?[+xv>uì1G m»ùúÁ{ï·ßšMoëZZEVÅoŸщõo·ë_~}” v#°í~æôAÁÒ s‡ø=^y͈áœ&:ÜÅ_íÝ«þíÔqû\ýÎ;žN9Ò3GíÔ§¯G¶£{ÿ’~à/èÓßL”ÜßùàC3ÄÏôË 2>Aæ~A¤ [ÆÓªéßj&쀎h§Ióèˆÿ¿ ¤‰?X×xy,ÆgxŽMúJn±TþÇ[Ô©¸“åòž'Ì›8ÓúHDa.‡E.?2°·mÛf"Cmð$Á^þ`¬€Ãe“ dÈ\jÉmvjQî4]iþüù!$0§á»Ù¶m[5pàÀ4ÒÁ¨!p,$6î‘ c=ïo×®];£”ЫW/µÇ ±íˆz¯Zµª4hݤlÄ<V!ΑÄÁpðàA¯MT!ok‡C²×εÑw›6ml3?Q?ƒ"•+iê!0~î\ï%àM¹zUH÷Љj)P¶œúAG`,ܳ§ÛÄ,ïÒÄ5ÅPƒŠžÔQ>n´æ…ç˸Ì^ºLµmÐ Mßl˜µl©·)‹Ì‘è–p_°b…º: Þ ÿÕÚµ7§Aeߺµ†|_ëH¡~üqu¶þÝõ›ÅÊ¿=³ëDÒ[Â}²–ö¨ähcº}³ÏÇÄÃkÍ»G^(ËigyÅ#éÂ×­£_™zè—·Á£F›Ó­{?ý…Ìã1Né#1ìÖY„nm‚Zº¦Q|ùs2éUq¶Fdû­G\v›Ì?n¤¬Ÿ¬ ‚@b€ f ²½ú¹nè‹/¾0Ò)H©0ýK?7ÙYºÞ‘+Ô&Öm­Zµ2ò-ÔåC2gÜúÙd5édp\“uM¢hV½zuÅ‹¡Œpÿý÷›zDžñÎDõþ˜ÈÔÉç{®€w›={vÔSÀ1‰ cì‚Ìãƒi9®A6ÙZ]ȌԓhZwADz”¼yeË–5Ñîoè´j¢·¹)uìØQ±muÐiOÐСCYL¸á=ôGßãí{á…¼=ÿ vïÞmŠf¼ýöÛÞ.¼‹èvE3n-Z´ˆ¨ÃŽÌKÓ¦MC¼«åË—W'Ÿ|²éžÊÕxK­ñ£D!V×aa÷ùçœÿ£>2U¶¹qgÄ8×î¶ÎH_rLúðc.ESÓ‡Ÿ´2ƒÀØYG^›ê¨‘"²yØm¬ïËÖ(´jõí6;ï1è)õ»~¨÷[¿g‡©_2›‰n/sõUþ&f]ù i4ß_˜|4-·¥. žk^»–w8ýâ,ðÛÀ—^ö6ÕБì8‹qX½v^H‚ï›uZÿ¦­[½cý Ç{ôð§EhýDZÞ^k_Z›¤#§Ö9ä¿Ýþ±Î²£x«µº8WiKxkÑÔ§8‡5*n×­[×®F£g8C¿@Bq¼¢õë×zœ4ˆ~²&–ï]|Î,½¹µÚIl£»A¢y­ÚQ ~ñˆ%…2®\¥êÝR%Íq«ô½¹BóªgçNê*ý{³]GõPˆrØZ휎”æ^³SgõN'E®å]Léw7¨¾Úqm£ÆÏÓ/0.anûMϼ¡~*_êZµF÷MáØò·5Sv»G¹ýÓ/¿ª¦LV+8¢×DW7ý¢„¡IŽŒËNí¬æVÿŽ;Õ#÷Ü­.ÓÑBñÈô<úüÓÖþÙ°ù#U£Buºþýà œu4*˜ˆááã'¨ó äWÅ  ”ê±ý0/§o›iòm²þMF‡¾rë6êá»ïV•u¤û?ÿyœÑï;ôY} ¿˜ÃØ^¿ê-n^¾ùúÒÆ1ƒ³”ÿ!®½iꪬƌßQ>'´ë­UÑ b©ÿënÄu>äRM?·‰% 2»Öº~=wU–A@A@" „{p2²ë­« ± Y‹ñr@dy¬ÑåÛLÑDX„ Ÿ$ jÇ6Š„RD® C’† ÔáŒÔ#&¿!áI¾KëÜy‰ôG/¬Œ~Év‰n¢ðý:êþ¾Y'-ˆÊÒÖvîÜ©¨.íF¶û™³o„ fr·-SÄÃh§ Qî=ô™ì1htá$ðkáÛýAsR°b%ÜÁÞM•Bߌ¢B¸!›˜mýõ¿Ž#E؆4”A@Èn±TäF®¼äâ¨ý•¹úju¾vLÚB¡cfÎLC¸+\Xkœ¯Ökw½®wöÙD´m„wl6Ò¿¿3/Véß &¿!_2á©§âí9ê±ÇTÝ.]ÕVí æºZ÷xÀ:³þ̃½ŒóÀîì¥#…:ëß âÒÛo.VCµ¤Úb ·uÉbÓ¬¼&Í!ñ!Ì>¬îÑ…ž°aºÏ m|³Óù3¤÷ƒêë}ûŒnúa­ñ‰ŒK]£¥ì^~ìテö§wÛE… ©áõUwôØ:O?o1Ÿsf"AýʶìA`ùÚuê‹]»½“·kØ@¤ÿ<4â»@àÐÔE‹¼N/½è¢û·CA@A@8šO¸[6fÔÌj¥£µΨöLTz©R¥Â51ÑÝÝ»w7Å3‘íL¤vF‡q«ÓÔZîo½õ–)2´?hd'ºZ³fÍ ‘ا_ê#‘íA}m#B}ĈaT¢&žv1tÁZ·nó¡AÎ Hw2IJ¿óJ"ܳw9KîFY—iº’5¢¥c1¾ŸÍjÕ¨„@Þ£\;í”SÔkš\n¡ÓDýìD†÷ÑÙV“Ÿy&"YÞJg)Í>ÌûnßœmøµÓ¦ª¯ ÿìmyÜ٪«Îv:UÝo]Ëtö»Ö±IcC<ŸPhµd‰âjÅ„ñê}]ððßZw9ÈÎɶÁÁ_8V_bLÆy9ÇÓ½z*"ý†Þ|_!°nú4EÁÔxZ 8ë¹çd~átÜó5éé§… (‡n£nƒkRÀÓE#¾Ë V®T?:EêÚHæe|–ÞA@A åø‡&›BÃ;Sþ’³æ¿ýö[#¹‚äHQ()t։ʶ†„ ò%Æ 3E-ؾUk°"Áb }tôÐ1$jЧ }R|‚¾©¤ìד·Ç»óÅ:‚ïMášyÛIñF&…(q®%Èá¡ÐN‚Ž@ó·ƒ Ÿ7ožò뤣eOõg*NGrT e3xð`u¶.ÌÍøüzôè¡Æ5›€¬†råÊM~œd'ÄjTþF6Çu\ ¥ó´& 2j[¶lQ—êÂ{ŒƒëȌᜠBw#q0mèÌt•´ÇÒ²I=Ÿìï»õo+ ƒ‰ ‚@ÎA`‘––©¥#¾1HØwgÏ2ËFDº#³r…þm,®µ½Ã9ÕJÕo 6êßSlþ=©qssÿGý3]P )–Òú·õ_Ú±š(㑌s}¸m»9O±Â…´ÌK°cf¿i-íw~½ÛNE¿Ü- Š¬ ð‡ÿ¦—¥q"Ø>è9UË«á”ȈíÓrk`õǪ+u1.ý¬0du¾ÔŽê=û¾Õ÷î<ê|W‰8hÍŠñË9bCà€–î+P¦¬W›)¦ÕZ P,1Ôî|»–ìZi:'f׫¥8m¨/«QSmÑ5+Vj¼*hÙ.1A@Hx‡å]¶_¿~ªÿþÉ1(… äéwÎSŽË-W›Õ×‰Ì E0‰Ì¦ )Ä;QÓ…uÔÑïâÖ u3jôãö•Þ~­ ']“޾µ§À$2zíhª© Ï¡{QŽ“¡råÊ*ZUûÇtÚ=S8{ðÁMD=ºôŸþ¹úóÏ?óÁVâF^'Vãó§#$ù‘Fg~Û¶mfBb†13A”3QˆÄEëyЙÿT?¤SõÇ R4èÈ‹eþ̉pd\ÖHÎ$ñBò¸Z@ý’Xûç~€9« ‚€ q:0ÁÚ)úÙ¬AœŠðÚ>e~ŠKÿñÇÞ‘îñ A@A@bF@÷˜¡’†‚€ ŠH„{*~ªrM‚€ ©‚À{¬Þ×ryÖ(„ é.–^™3Çë˜ ™Ú•*yë²ÚÌÑŸ=™½XþüùU›6m̲üA@ô# „{ú1“#A …ð—±I™úpåRA@r<£gÌ ¹†v †¬ËJüجÉÖ¶nó:lZ³†BrJ,¹xíµ×ÔT}]Ü6£2—\áĉÌ%Ëd$ áb‚€ ‚@ÆÂ=c¸eÉQ>}ºzO;s mñY³f™"jèÁópEQ1A@H?ý﯃ŽÑÊb‚€ ³¸éºRêÃ… Ì OÒµ12bSuaèú5Vèœs2Ò…#qFÝö©‹y½–(Z$l½¯‘,d7ºNZéÚBbÉu«n¹å3È%K–¨ªU«&÷€et‚€ ‚@.A@÷$ý )*zûí·›ÂŸþ!þ¦ &lÅŠfª¤Ó=K”(áo*ë‚€ ‰pìr§žrŠº¬X±L´èùçgêx9XâÀLM þxð ×q§&M¼eYˆ/þù§š4ÿoÇ%=sO,sõÕñ=‰ôwÖ®]ëõÉrf÷Š+ªÓu±q¬P¡Bf.A@A cáž1Ü~@)ÞÈÃo4£ÝñÇ­™ì¤hj(²IA@H\9™ãŽ;N5«U+ F•šCXºæMµoÿ~ïâ¤XªER/üþûïÞøÜeoc:$c:`ISA@A B¸G(»v,XPM™2Åèè}ðÁÊ_Ø‘q¡5}ùå—«º:ݳH‘"Ù5T9¯ £ð·DÃ=Gœ2xA@Aà˯¿V«Ö¯÷®¦nåÊ*_Þ¼Þº,Ä¿œL˺uâ{é-W °mÛ6µ{÷nUªT)/ZþСCŠ÷Ù?þøC!‡zÆgDÅ‚ ³;v¨]»v©âÅ‹«óÎ;/ê1±6øöÛoÕæÍ›UáÂ…å:VФ ‚@ºÂ=ÝeÝ 4PLb‚€ 8þ§þÒ¹h¸‡À!+‚€ ‚@¶ 0jÆŒó¶k(ÏÄ!€ÄqåÀO?©ùË—{=V(]Z] µ,<<’miÑ:¨Õ«W«~øÁÞ³Ï>«^zé%oÝ.@X÷ìÙSÕ©“Ö‰2räHõ /¨¯¾úÊ6÷æ«V­R%K–ôÖÝ…±cǪáÃ9ö´@IDAT‡«;wšÍW^y¥Ê«b3gþ]äø-õF¿{öìQ<òˆ‚tÇN>ùdõðëîÝ»›uÿŸ-[¶¨ûï¿_qn+¡J›ÿû߆ÄïׯŸ*­ÿ?ÃQú‹/6Edm›† \æÌ™£ú÷ï¯>üðC»Ëèß3N ^ó ‘…$Cà¿ÿý¯Ú¸q£¢^Ã7ß|£òäÉc¾\pºôÒKÙ_b©…À¦M›Ôo¼áÝ7Q³¸ï¾û²ì"¿øâ µuëV#Q–™BÜY6à$>‘|;“øÃ‘¡ ‚@â÷Äc,gA@Òƒ¿ÍãçÌõ)xv>U¥lYo]â‹À´E¯* ÔZ9‹DrÎW®\©&Mš”fpn/wçÛo¿­H¸÷íÛW}÷Ýwnso™ˆôpiNÍ1kŒÉµ_ýUµjÕÊÝd–!ÞÿóŸÿxˆpkÔTzàÔ!CL$¼ÝnçûµÜD:…a;uꤞ{î¹4DãǬFŒañæ8êׯ¯ Üý¶lÙ2uÓM7©O?ýT˜Á¢ëþ>e]ˆ8°ø^@¶Ù"sçÎU7ÞxcÐî”ÛÆ½jÖ¬YªZµj)[gâÝwßU×]w]Ègwê©§fáÎ}êOÚÏý´K—.!c‘•ô!pLúšKkA@R )ššZŸ§\ ‚@ÎG`éš5j×Þ½Þ…´kÐPI”•GÜ\9™“O:I5¨zKÜÏ!ÆâZºžÁi§f$FmÏÈ"RÛËøÞ\rÉ%ê®»î²ÍBæD¾ÒRí1!;#¬…~î¹ç†´  *Fŗճ믿ÞkOT¹k8žzꩲ½˜.†Þ¸qcU¢D ¯)ÏíDå<ØÛf¸ÎæÍ›«3Ï<Ó\ÝþÚk¯…í'éÿqŠËR3 #jøçŸ¶Íe.$ .4ÿÿáÈvùã?ª¾l°¤|‚Á}ø¶ÛnKв¿[î£ÙiÈA¶co¾ùfv%%νŸfJ@(!9¿þòIÊdó\NÆRÆ.‚€ Ä·X*ýµ®_/ÝJ|ª£ßÖéëÖ ÛOÕr bÉ‹²,óçÏ7¤ÈË/¿ì ´G ísw"Z‘Èïp2ÈØc ¢c1|"Î]ƒD'ÒÝIθ nˆN·d’+ë‚tBŸ>}¼®ˆF‡ß¾}»š6mš‘6@w2Ýöìw ‡ÃĉÕ÷ßoÎåîc™ý8¾Öõ! QíHÛP7í¬³Îò7—uA [¸óÎ;• »úê«UïÞ½Õ€T×®]UíÚµÕUW]ed–Úµk—­ãÌÊ“¯]»ÖœŽ(p¤vRÑ®¹æsÏæ~šrA‡ö`u—½².DR&]pIcA@H5샌½.)šj‘¹ ‚€ õ|¯#ö8òo¸A=ÿü¬H.9£ÝÎ%‹œL.ùàã|™åÊ•3R/të’×;%z;ýôÓMTùï¿ÿ®˜¬3Æþ¬ßrË-†G³Ø5ú7nœ‰DÇÙ€|ÚðAÒ:îqv xÈxס€6Qõb‚@²!€ÃÉJ6ñ¿O½dEr»Ùû†uÚ,•TÃ…l¦{ï½7$ë'×Éý™îá8;ýr»{u¶!²E‹-2cÁÉÏ"Ö‰¸¦dêS÷dú4d,‚€ åüõ¿¿BÎ)„{²"‚€ d)ãõ‹¹ÖÞÑyÎÒä‚“t0qÞ|ïJÏÍŸ_Q0ULH/ሯX"4‰Vµ¶aÃÎÜ{…$c±‹/¾X¡Õî—À‰åXi#dîÖÐÓŽ7ÙNÆÈæÍ›UáÂ…c*Ì÷î³Ï>3Å•‘²*Z´¨Ê—/Ÿbºæ¿üò‹ÉZ¡ÎÃe—]f À¦«ƒ85FŽç£>2µ.¿üò ×pàwç3çèbãHaÅrßs/ƒâÒŒSŽÏªz|dNàÀà>L ^[#ˆkrÇoޱmd‘”‰ŒìGÀýIñK•ËA@’q³6ü—Žè«[¥rÒ9§p¥.¦ù•~Ñ·Öª^]OòÃn“¹ H¼Ù¶m›wŠ~øÁÈÉ@8MÈÅXƒà"Ò=š!½!d{4”dv#€ÔQ½zõT~íø¤Nƒ5dš¨KàNÅ‹‘a²m™ßqdžDwÛShCîéŠ+®PgŸ}¶ª\¹²!ÎÉú§¿oß>…nzÁ‚Ž+Ú–)SÆ_¾|y5uêTÓ¯ÿD.2Vµvd²4kÖLýë_ÿ2EAo¾ùf“ s饗ªuëÖù»ðÖó :Ó~ ¦­(PÀëÛžƒÈë–-[†dÐØö̹ß@*ÓŽc(6KRœîýúõSádT¸VÚÚsU¬XÑdØÐŽ ²s¸k¤ª¢Ý›( Ý­[7C²ƒS•*UÌgƒs¥I“&Ê•Ýr¯!žËœË/7†#ÄX¸\ ×zë­·ÚÝ2‰p$i"©‹€vJ{fu%½ ² ‚€ Y†ÀúM¨ÍŽ.s‹:uÔI'ž˜eçÏm'òËÉ´ª[7·A ×›Í Ù´k×®t鈽¬ŠM÷åA Ìž=ÛHwø$&Û5Ö{ì1#9’7o^oõFŒá­Û…ºVr!þú ì'û‚–û]Zºt©jÕª•">ȨËÀDÈô3Î8ÃkƱ~2¾}ûöÞ~wÈnÈ{ä¡ šýöä“OªwÞyÇ¿Y8p Í6pA: Òܵ÷ß_µhÑÂD’»ÛYc¢Ý™&L˜ ˆâFbÅ5ðf¿5!L~û¿ÿû?õôÓO«+V¨·µSÛ/Eûõë×›±ìرø‰6Ÿ>}º)†ë’ÝiÆi×ûÀ(Ή´ Ÿ‡k8jÖ¬©4h`Šu»ÿ#n;YF@"܃q‘­‚€ KpuÊDN&—|èr™‚€ I‰Àè™3BÆÕ¾QÃuY‰¿êèºÙË^ó:¼^ËÓQzb9¬ g!­ýç?ÿ1äX,Úìb‚@* @!T¢¦O:é¤4™F…¹ê.vÉv0@÷›âÂDa»d¯½öZÙÎ9ˆV·RPd“üüóÏŒH;ÕÑoK¶Óò/7V*T0õlcô½Ùî¾SWªTÉÞAd3ĉ•FÊÆÇ=“hnˆv;®‡hu¢¸úé'#guß}÷©±cÇšsñ‡}dx 9¤;Zöd&@˜[{þùç½û…FÁÚµÎ;«ï¾ûÎlBІbÐHÛXCƇ6öžh·ûçï½÷^`6ƒ¿]Ç9I¶@8ãÿÅ-"M{8cø.\¨$81zá· áÙ“dP´/:©R®‘.EÉm'Ë‚@nÑÔ5m‚€ ‚€ p ÛÒòÖ:5ilež\9™ãÿùOÕ¸ºè²&æ„wéF‡C!…!ÓqçñDuŽ5Êìïß¿¿!ž,ùÃÆƒš}ü!JÕRD—^{íµjРA&Š’È%gÖ®]«êjI"È&¢L­}õÕW†ØBb(JkHÁ) ™¹4cÆß.DîBr~tš‘h€ôùàƒÌõ°ŽA¾!•af¤ =j·¸*2 .a iFDt¢ -&ä P!ã!š­Aò»ßc¶/X°ÀHe'ºò®q?@b…ï'ßWløðáêž{îñ¾n{–ûôé£}ôÑÍDO7Ô…Ñí>ùä“4„{ÈXᙌq¬ŽßÁõ@¦ÏŸ?_U«VÍè¯sÃA@Ó #Ëà7Þ0úöîþÖ­[›h}"þ¹׈°'#ÃY!Oô¾kôË=«lÙ²FzÆÝç.s]8Bb5Èýp„;H~ˆØ¦Ms 8Y¸ß¾úê«fL|VbéC@÷ôá•îÖ|ø¥7$±Œ!€×³F!©J¶'nFb‚@F°?,/^ÛŒ¢(Ç ‚€ ™CÀ•“¾- µ9sg£-J]¥5d­ÕÑDçŸnWežƒ ZèÍÉ“'›÷$Ȥ f. òÜJPœ”h÷Hæ×H¶ääd=ѱֈ†…4G ºC‡v³Ñ#†ìFãù™gžñ¶óü½zõjCÐCêŒ9RíÝ»×hBÓˆh|¦HÆõ¸Ïñ\:Ô~cl6Z×î#"7_¾|B¸[@džÒ@êò=Œ¥xðâÅG3Íz÷î†lwêÚµ«‰lG¾†ï¬Ÿ@¶íÝy»9ÑðÖÂ,µû32÷ÝwCƒdî=…(òp„;?ŠÉú §&>„»ÿzèÏZ»víÂbÅñãþh{l¼çŸ}ö™ùüè·D‰Æá‰´ÍÀ½¬œB¸§yÑOH?f1瞇 *EãEw¿¼1w" ¤¹º`‹ /þúëhÕT!¯¾¥A@A@ˆŽÀg: öÍ ½†õ«Þ¢Ît^½²&Ìý[¾Ãv&ÅR-9sþòË/›hÄN8!ð ÙˆB%Bœ(t gÞQcÕ@‡Bv© ¹WZ-fdÐOvgëR¥JyÇÙ}¹\©ëXò ]g¢E‰*…dDÊ£õ¾fÍš]g4–‰âŒföÜDqŠ ¹äib!ÛÁ‚ káHr»Ÿï÷u×]gW=ÒÖÛ n¡S›-CÆL¸ÉuÎ!_O#ãÈ÷ÅHvÍ5×D ä^‹$O¬ÓæÍ›Ãž§NT"ÿq¬Zyî©82 æž/–~$Â=ý˜Å|^{›òFª…Ü´¿˜;’†æ”RpÐìâ…€ë“÷x¡*ý‚€ ±#ðò´é!ÛKÚrñ^$¥~óåÍ«ª–/ïSHYˆšÄè'ó®©Nä9Ï´Dš¢¿ ¹dȶ0eÄ(dÈÎÜçk·ÍoºVC8cÌð-[¶Td‰£=Oä%5D“B2§àcmË$&GÀ8ߣ³¡¢²QÖNOÂL)ds2jè¹ÇÓ\|ÎdÜ+ÃÝG9Ž ¢éãeýúõ ì ­~±Œ# „{Ʊ‹z¤ë¤1i6B¸G…-lƒÇ\1½ùæ›i*O‡=HvQpÈ$Â= X²[A@ˆ3h‡N8R0‘®ÏÓŒ7;ÅÙâ|º\ßÝ:ô©&3­5×QrV§Ûn“yÎDe¿ÖrN¼žÇ‹)b¦œ8~³ “ ~æÌ™æB†Š – #+åí·ß6»ÐGÇÁ—Uæ¾ÃG:§Ë¿¡þÖ[o©B… E:ÄÛï`ùÄlkÞ¼¹jРÁfÿþýæ8Š!ÃÙ`Ó¦MS522T•æÜpfÈŸþùªzõê¦ *üòÏœ÷wÞQ_ýµ9ž?ÔǰE ‰FÇÉ` ù.êP Å‚Æ9ÆgÕ·o_e±g,*TPO<ñ„Á9¯–p{þùçlíÙ>uêTU±bE…ô × 5õ)\C~†¨|ŠRS`úÄOtwËr#a »K—.MS=o_F×è_²d‰Ú²eK/QŸ>}Ô…^è6÷–ÑŒzõÕWÕúõë½Z»“/O5Ì*ņn¾ôTF¦` Å{ì1U®\dMBHwŠZÂ^‘¬kð]ÔuäX‹s‹- ±‰Á[=ÚvNmŽñãÇ«Æ{ ààð7K1LüÈpÂ1‘žÈt>#þƒÒŠ8'ÅVÁƒ­uÔøÇb×ñ¿aûöÏùŸÁ)`Dù0`€êÝ»·¿©¬'7Â]÷,Ý ‚€ Œ›=ÛKGgwûF Zɦx!àÊÉ@$4ÓEb‚€ ÙÁH@ÍÐJ‡Gó[·nÝÔàÁƒ ‰ëßç®Û÷6mÚ¸›C–ᾈ'‚>Ç}g¦!Ř …pö«,°àS¸<{­ðs7Þx£9R/ðTæBècG¢D¶³ŸÀRx.8@‚Rý†¢lÙ²ªY³f†r³×á£\£ÙúÙ#œÁCÝ|óÍŠz$s°‹.ºHÝvÛm&ÖŽ•틺…šá wìØáq˜áp†‡$Ð’c‰¸÷ŽN:þÑò¨ÈéáÎ$–sø‡þü/½Ã…¼´^ÿ±|ÁñˆY[¹r¥ùǶëþ9^<ëíq÷áy"uB6È ‰ñ()͈8w£îñ’ÚÉ›£ÒûºuëÒÜPlD(Ó&3Æ{âĉæK¯Hg²¦L™(³“HœÁ,*»úÑð!â#7Ô ËlÑÔŽ;šTœ r5QÿA×Á¶Q£F)ÆãZ], §‰õ¨ºûbYæÿúÒK/5ß<Ó™±#FOp#ýc;mhxrfΑÝǶíÙS½2{ŽF^ý#ÿÝú¿ ¿d÷¸äü‚€ ‚@ª#pÉ­·ªmŸ}n.ó Ö½ç­7Õ ú…Y,þìÓ’ç–¿Ñd€Ò{MM(Ì]ÿ³¦~—Õ¨©¶è`(Þyy¿ä@›wYcHa±Œ#@t5 p§žzªQ(€Ø†à΃ÂDÖ†èpø@äXàáŠ-Sp‚Záî˜ Ð‰Î'RŸ ²;ˆ'JÄur` éNPmžE'pPeBºÿ;žðGËsÍ~}³DÿoØqºsÒ´ªT©âyQq0 –uüõ×Ñ$7í,ëF gA@rþb©mtzµXâ¯3”­åÑdîb‚€ ‚€ ‰A Cîv(ï¤\ÄbT".¤µªýúN±KŠGP$0Ç’öAñ€ N zR>ú¨™Üþ!_‘8qÍ­<ìnGcÊj$¹îjj#B„<ÅYƒ Ò ãp ¬À =F_ Ls²+Hå eï7¶QŒ" Œ¢8ÂiâÓ&^8Ó•¥]9 4º üI“ñ8Qm™Ï½/[•œñíÍ óq6ýŸP4gQó63íôÛo¿Ýs¾dÕÿ†ÿ:ø|—-[fèµñ?Ç÷J,ë‰pÿÇ?²îÄr&A@A —"ð«~›ád«^«Ÿõ®¸¸D.E#ñ—ý¾~oÙìèߦƒ‘$› ñ¸ËA@A ÷")Â=»a#Š{úôéFš$–±@ººäZåÊ•MP¡·¿º:ÅY¤Z°Ï?ÿÜ,—.]Úm–¥ËDÚ‘ívÈ© ピŒÕ¹§pÑåA¤·=.hž^œ‘óÁ b É"Ï£©´¸(k(Eh»ÏSÙ9ˆl·m ±ÇŒ£ þ­DdƒÐÇÊîÿ «fÇ+ó¬CÀ½'H„{Öá.gA@ȽLÑAîÖÚ)Lf×e_l­Ûkëz"'c±¹ ‚€ ‰@ ÇîT Fª†ÈùX RÕ5¤Z ¤3bhƒgáŽ^;–£7Õ-a ±D¾ÇjÁbßÕöGïÍöX 9¢ãm‘Õõë×G=ŒÏðV]t+šOÅêçž{Î4E«Ÿ±‚gªüoDÃ@ö§Eà¯ÿýåm”w YA@†Àè3½¾OÔ£MjT÷Öe!¾9ŠƒÃZ1%{Ý%íªÌA@A@€À1 è3Kº¤ÀhzÈv…–w¼ŒbŸÙeHÜÄjåÊ• i ÁœËÎ;vì9EzŠQ 9tá…zÇöÙgÞr¸Æ«A®»fÿ'ìÜÝ—ÑåìüßÈè˜sóq!îÿȱ·ÄÜüʵ ‚€ ä ¶ég»õ:pÅZ£[«)4ÅŃÀ««W«ït}#km¤Xª…Bæ‚€ ‚€ $ \Å.qÆqò›o¾‰[_éíÈéxtÍ]£°h¢íØc 9…Kh†ì³‚Ì‹5ŠîF³SO=5Zo¿¿†Àï¿ÿnö¥Êÿ†w¡²3nÑÔXþßbîX ‚€ ‚@Fi™F×Ú7lä®ÊrœxeίGžsZèÌS1A@A@Ä"»KbÇ‘%½_sÍ5FKÜžìÆo̰¤Lz¢ªíùâ5'‚msŠ~F²O?ýT¡õîDmn„:çzíµ×Ô}÷ÝÓi¹¶;wzmý}y;œ¿SÁÙ•fÑßÖF¼§ÊÿFš – QpBB¸G…K‚€ Fy“‰óæ{Ç>ï\U¾ÔµÞº,Ätí¦…+WyVºáunþüÞº,É€ÀþýûÕ²eËÔW_}å §víÚ~O÷:‘A@A ˆ;á~øðሗóË/¿DÜŸÈ~’üàÁƒjøðáé–¦Éè­.yFw»ë®»…@Ûµk§‚HB4æ7n¬~üñGï0ÈeІ&Ú.¿ürSÈ–‡'lÉ’%jìØ±ªmÛ¶OM´yË–-CÚP°5šQhuÕªUªB… ›2žqãÆymˆÄ/Yòo Ëìþßð% YŽ€û½”¢©Y¿œPA !0ù õí÷ß{WÜ¡QãÀçX¯,d ©‹)jYkU¯®]”¹ 4P_lÓ¦M!ã9ÿüó…pADVA@r™&ÜóæÍrÍ«}úô ÙÆÊÚµkUÿþýM´sšY´áúë¯WEŠQŸþ¹9#¤4Å6§OŸ®¢IŠ@ÊmÙ²Å< @\ÇRô”SN ¹27r;d‡^!ÊöÍ7ß4cá<Ý»wX”¡:¨Ñ£G«V­Z™¤U¶mÛ¦V®\iî?ÿüÓ; D"ÅH³ÂN<ñD}?`Àït]ºtQ¿ýö›êܹ³ "5wïÞmŽyûí·½cÐè¿óÎ;½õp |6-Z´P ,0PƒÚ!D›={öx»Ë—/¯N>ùd³žÕÿÞ Ž,àÙ¾}»Bï>{YÿSÿó:‹AÁÈk+ éG€{Ò ý?ðÓOé?XŽA@Èñ<9r¤w Œœyú¿ÔŒÅ‹½m²_†ŒçuHqZLðö ‰ËÂO?ÿ—~rs'~9ÒÜŒ…\» ‚@ê iÂ)Žã?^YÝmˆõ{ï½WÕÑú€D,Cj“"¶|ùò@Ô~øaÕ¬Y3ÙìFj#-2uêTCBCÔþàû¡£—_~Y-]º4¤OŽ'b™´J•*…ìcruÒ¤I ¢Â{ýõ×Õ\ š4i¢ð®,XPåÉ“G I»yófµ~ýz…núOI4Gë!Ö­9Jl\{â‰'äð :óÌ3Ï4çØµk—!ò¹V—ç¼Ð`ÉÖ­[§˜¢ÙÀ:~KΜ£[·njâĉÞ5ñ¿ÐµkW#qñ}É%—¨³Î:ËÌ7n49¿þúkÈð¡9ï¼ó¼m8s ÕùóÛ×_­®½öZÕ¼ysU±bESx⟢«D}þùç•›]qÚi§g…í'«ÿ7ìy™3æZÏ”$þÿȺ¨_¿¾ÛD–ˆ€+)sŒMM ÒJ™9KuîÛ7¡çÎA@rüþvé×?g 6Fù›~oõŸ)p%Éy ¼·‰e äGá † ¦Ö¬Y“±Nä(A@A ÉÈ4á~‚Ž–€È…¸Æxx&’:ÖhjHs&ÈW¢Í­!•é÷‹/¾PL~CZ}sˆì|ùòùw+"™zè!3Ù?ëÈ´ÎýzçvМƒh„;ò-hCjcD1÷ìÙ3¨»4Ûˆrv¥.Ü8\’ÐÝç_¦Hè!CÂʹ$ g2æÏŸ¯Ê”)Bt£¡î×Q÷™õêÕ«+ÖpFÔ¨Q#,&´¯ &˜Énþì³ÏšlwVþoØóò?>yòd»j"ðûjBRw’„/HÑÔ„Cìàë}ûÌr}o¾H;:ÅA@r_é,Ã:@ÂÚ%úùßgœaWeg¾ÐïB»tðµ’Å‹«<ÿú—]•yœX§{þ«¹¬”fœºÍUÝiÞ°aCóîéý?W"+‚€ ãÈ4á†DôÑGdïÞ½!Ç]žQ#Úž‡ž Â>{÷î­ „™ÐÅ„ÒkH·´nÝ:êa¤ÇÍš5ËD´:t(j{Û @†ü'B;ÈÚ·o¯*W®¬ž~úiyÔ†c‘céÕ«—‰¦jöD⌖û[o½e$oÈtˆÅp4!+­²Ouá±ôkÛ½ðøã‡u@dÕÿ†OÓÙ2pf‰%×yåþÏ%þ̹÷ uõýëùþýr/rå‚€ äBŠU¹Å»jˆö÷çÍ5õˆ¼²7xf¾à¦ ^œsŽzþ<ÑË÷‰ßÂE•«¨ÏœbŸñë9ë{"Û–ì`Š—’ \´hѰïÓéÝ·ß~k2Ç .œ&è)½}¥§=ç…£ ­X±bæºÒs|<Û|ÇXxOç=9Ü»~<Ï®¯dK¸1ÊvA@r:ÇÄãˆN' ¬téÒa»+Q¢„zôÑGÓÅDÎ-òÛn»-äX"!•ÓóCd%e8’%á R y4³!Î])›pÇ@~"Sƒ4 ðhmÇbHÜ Btv$£ÿjÕªíõO>ùÄDx‡k¶98ï¼óŽÑ”Gº…¨ý{î¹ÇH§ðYÖøÔSOE$Ûé?‘8Ó?×΋/¾˜M†tQÝèØ<8ÍçÎÿÖƒ>¨ÏÿOÿþýŽŠH†” ÷èɇ³¬üß` ¶ý÷¿ÿ2²„l$¡+®#'–{AB# ‚€ )ˆÀ*-ϸãË/½+k]¿žíñ_X®å&mV½·Ò2˜òŒœ³ºGˆp2¬Ï>ûlóއD)Ä-ïs~ã}™U·-ïeȶøÀ¦ûï¿ßH[ò¾VµjU“¥Ì±H±"}Éî¸ãC¢3;uêÔÉ‚ ëW\aÆÁ»=$>ýÛšj‘úeÒ®¼ƒó¾dûæˆøï¾û.ðp‚ïúõëgÞ ¹ŽGzô_:ÃsF=6Û’ž52ïÕÈ⺘ùejǯµÇ2w%PýBÆ–lsÚÐex|vŒóðáÃþÃÔNÝ,cI38Ù ‚€ q‰pçLD“Sð’ì7ÞxÃü8MŒi~h­ñCÍ—q%f¢µÏÈ~Æm‹ßØß³gO“ÝÍ~ÖS¦LQÈÒ†3Æ …‹µst-$UýõWë—EƆ,jŒ€&k¢Ñ‰^ǹ€!¨5Ýp$@†¯ZµÊd¾ÛsúçT\ã‹ä, ÊÞ’íúd»“a@D/8°Ìg†sÃá’n]tQҌŬg-©…œª×é¿ÃuVFj—¨}ÔU´Y;d.‰%!ܱœA’7:%»“&š ‚€ !ÆÌœåw²Žrm¤k‰%W O8A5Ò²b©…r,ÖÖ¬Yc‘{±¹üᇚÕÕ«WÛÍ!²• ,0RÙië±]ýõ^[ Äótu߇îE‰‡4XAwÒû‰'žð"·‘¥Î쑌¨^—l/S¦Œ!ÍK•*xåÈè`Œ›Hz꣹–7o^5zôh#uãn÷2Ñô6¹du¨çF6kŒIdc,qÔ-µÜâiÉ4–x^Wnê‹úv8j'J%ã;BÆNn’ÓEé.¤²ÜûAV}®n½w9«ÎŸÏ#„{ê£FR·ÜrKš›?ÆhŒÛ"0Ï?ÿ|_… MHnB#Ü“{¬2:A@A '!ð‘&6ê([kMªWW§éôi±Ä ðÅ®]jÍ»¼ÎëV©¬N×2"b©……7­Q;hi$_\ƒ€ß³g©mÅöüùó+¢¦­A„[ëÝ»·Ùo×ýó®]»*¢ë1äO¶oßîo’fúYH¸mŸs ¢¢1OÄ=×Έ|·†< r±AÑýÈ#íŠÛ6d#\#ªžhü ‰,÷]ä½÷ÞsÍôr2%Ó“K;À!ƒüJuýû™J†|Ž·pßÕTºV÷ZÈ&ºýöÛ³„p8p ‘°!{†ˆv7Ð1íÝ»×ÿ5kÖ4Ê]úB,¾u³Æ·_é-“Ç‘t5¿ý¦Ór™0¼÷LTP§@¨˜ ¤·hªh¸§;i-‚€ DBàå#§¶ÈÉX$3ï#][׫—˜I¯ÙŠ€K¸á>yòdOÆ ™—Èr rÒÆ-üél·}Ø9 èïØ±Ãl‚t‡PdœûÜsÏÔ$æ}ÈÜ Yã—q;°R2ls n» á…¦|¢"<Ý‚­èç#«‘™ /K¦±Ä뚤ŸPpNmÛ¶M‘MÜFé1HÖ¯¿þZýðÃ&¨³P¡BŠì”ôŽ#¾‡di KŽ-Ùa8!ùßÇY&6‹$½c±×„Œ×S¬X±tå8>©Ï@À,Çgt,éû/ºž ÎTøDod rÊ)^7H[qM®ÃâÍ#?$Â=~XƵ' €Æ*oA;n°b‚€ ~Ü¢©V£2ý½È‚€ ‚€ à"Ñ4yÁBoÓEú%¾Œ.^(–8&Ìçuž_ëcWÖ2b©‡YÎÅ‹7Y6hÐ ï"­&8õC=ämwIz6¢qn B(š}õÕW^“ÓO?Ý[NÔ„ïÑòè«C*†3Wjù $§{NŠD‘íœ'3$£ÕWvÇ›™ådKf®CŽ E€Âã8 rù~ã8£0òe—]f¤ŠB]£®Aݺub…Jù~!{Ä÷‰)Šó=ŠÅ>ûì3E„4÷[C‚ã!î‹-ªzõêe¢§Û¶mKw¦ …¡ (å>@ád&nDÄ»Ù/n‡…T¦4íÁG!˜@º÷ë×/ì÷~êÔ©¦­=WÅŠÕ¤I“L_\ª—^z©!¬©Aaƒ_Ýó»Ë‡2E˜ùl µÉP¸âŠ+Œ#¢I“&žä—{L¼—qzÜvÛm!ÝRÿÂ÷@—l·[E‚η¹îqƒ2¾,XГ¡à _n¢ü7X¾°Ü Š)ßHo‚@.AÀý¡‰ÕÉ•K ‘ËA@2ŒÀ­Ïú½Ž2³Ö±qc»(ó €”ÌçN:xË:uŒ–uN%]&®Ž»%Ã!§ÐG·f·³î×Iv£ßŸyæέ¡X+Fï ‰6Æ„ìu 9 ‰®Ðß•W^éEoBÖ!¡”)9ؼyó˜‡ÔG´ƒ¯v‹8GÈ € ŒerõòýçI¦±øÇ&ëY‹À‹/¾h oݺ5äÄ8šÐ¯£ïÿH1¹«m¸iÓ&…ìÒ¼yóL!a»Ý¯]»Öä³gÏv7§Y†à†Ì^´h‘"¢ÚodÒ<ù䓦(1²Èd«Äbhœ£â€ãhu&2EfÍšèLÀ)¡Í÷Þ+¶çá{G´;RRðjA÷jUnÏ…„šùDþ»F ÁÓO?­¨)Árqÿa,Ï>ûlçßajMp¿ŠFÚõÞm&L0˜SX;è¾MÄ;|"c"3Á½w¥÷\Ò>!܃qIŠ­ 40d:7ÒPü7S¼qñL;KŠ —AYˆ€#îY¼œJA ¥p‹¥¢›Ü¢N픾Þì¾8·X*ciU¯nvIΟ@\ÂÝž"’ÈÎzÉ’%m33oß¾½—! é²oß¾6¬@¤U i…ÑíåD÷ "\‰F'BCò‚HQ¢sýFB7Ò² §E^mqFÈ8¢ý„œ¿/ð²ï¼‡8p ¤É«¯¾ªJ—.–0ãVêLkÕªeˆC®)h‚HĹÀäqÒdK²’­¸ÿ'd1­ŽÆ;k­Ýºu³«Þœ@M¢¾­‘íÂÿ3÷¾ëV Šs´k×.Ðqűæ;e³Ei¢¢ª!”ƒÙf‰Åˆ4÷R9AZöL®]»¶áÊì1DwÓ–îµ"‡…³bÝ5p""ßo\ßgŽ!ÜÿСCíª7ÿöÛoÍ÷ÝÊoqáÞ _‡ÓÓJº¸ŸŸwp‚?βü68ù¸¾¬’ºñ!Õ×EÃ=Õ?a¹>A@ˆˆ€«á®ÅA@A “ìÒ…¸^×rÖjÞ|³BâD,1Öµf8E0¯Ñ/Ø—:2sVé5;ðKÄ0–6mÚýfˆ³#FxÃ#ÓO~AÌ=þøã y òŒˆÒjÕª²BjÆ ŠHWk-å×È´†°!’Õ’'Õ•¤€x"êâù ×FmÆKp™ß «,XàE^ÚXgËÍO?ü°‰6å8Š» o‰w¢iï¾ûn—Q×! ‘ìA†k¹Üd‘þ½¹†|mÀ†(|Ètõ‘ø –(dd; DÑp†DGcçΊb¯níË/¿¬:tèàuŸLcñ% IôàÁƒMÝ?ë âÿ•hí¢|Ô¨Qªž®åá\%kâ™ïÿçÈž¸ÆwŒ"Éü_æ(BX'˜­ ÑÎ÷£O"Ö feL8¸8ÇØ±cÍ~þà€¬\¹²*äîÈááï^ûóÏ?¯î¼óNsÎÈ=zx}°Ð¹sg/ª)¢úÝ{58Ú]É(žìÞÃ#µe÷C2 ÂNG7û‰ö|†`ÔÏÂ… ='c¸>d{ÆÂ=c¸ÉQ‚€ "¸îR45E>T¹ A ؼ}»š¹d©‘tørÏ×*ož3TÑóÏS—]TL5­YCx Ù0ªÜwÊ9Ë^Sÿýãoi„+µ_1ý«½þÖZõÃÁ¿#Ë_{­*pä%t¯&YÖhâ ;óô<ªrÙœ«‹îcÅ(ÖvctªÁ%ÅRcE.cíæ¾öºúÙÑfm¥£ÅR¢P‰TµÅO!Ÿ‰*Å ¸\²Æ/'c‘¹ÿþû ‰ Ã÷r ‚,Ȉî„D·Ñ¯¶ „wPÔêï¿ÿž¦ë믿nÈ=?áN‘¾ èz{Ž7ÚE3g¬Öôq„CÁ¤¤vÿþý=ÎîcN„9ÄÛÒ¥KÍõºûÜet§q`p. ™2…3Ú »óÜsÏÂvD׿ôÒKêŽ;î0²àiÍŸß’i,þ±Ézö!@„uÇŽC@Ýþ÷ù³,Ž —p瀼yóšˆkHumÜGøN¡ßŽc ’Ù:‚¦rIgާ(3( î ¶æ‚Ù¨ÿ@ì3>²Tˆ¶Ç¢[æžäj´óݦoÆd8­ “1䮸ޡ·îß)Èôùóç‡"ˆp¾WÜ+‚ ‡$ßidž]kݺµqâ‘!‹k80æ)\N´8„¼ÿzé—ëÁ1‡ôL8ãºpDÆjûöóöƒÃ’ŸûÆ} G¤u’±Ã˜Èn‹?B¸ÇSéQröLJ!Ûè€4|ª d3Ÿè(¡{<¦–êŠpÖ[GêôÔÈ-[$Å}fÓ–­êÏ¿þ4ý\¿l¤Ráõ6<àO÷ê™.½—Öåܨå°#GªG÷÷>Þ¢šv»×l'r8'îá®Ñ\\œþð»úÊœ¹^ogkB¬šN¥K®œ Q}·i'ŸXê#@Tµ%Ü!Q¬A¾ Š&1ŽpgßÀ GAA&÷¹˜ýH;  ‰Gd¶ßb Ê–ˆÙHf#ÜÝqÚöDÙ#ýâjÎÛ}Ì)äw³Î’!Ú"£?H@!F¶­J”?Q¤D£ãà: מ€œ"jC8ãš‘Ñ!šÕJfضDÁU‹£‚ˆâpcœ£ø"EÒ†˜äú¬¤‡='ÓXì˜dž½ð¿ì'ÛÝá#ó„ì 4Û!ˆÝ,jPTyÊ”)i¾ón?á–?øào÷?ÙîíÔ œ‡Ñô~ÚmDzK¶óOÆG¤gT{³ýð‹dîý(òp„;F?ÙN¿HÕ@àƒ§ÿ¾@Öâ w­9Îw>+ŒÏÚÞŸ‘ºÂ1ˆ´ ÷þ{ïýûÙ§…î‰ù4„pO ®Ò« äÜ^÷A$‡ _†)ÙˆÀ×ßìSUÚ´UÈg¸ÆË·{o!BúÞûíß«Gô4Ýc³b¹œÖ×&$=è/’%ÓX"Söe ×ê,¼HÆw‡HNESÜ’Ë|ב\ Wô3R¿vŸ›ñá`¶ûÝ9ò4d”¤×„¢hjÂJk\)±×ÏZˆ86¬…“¿±û¯¹æówßì>æ8þùgwSÄåH÷>w¦8,‘ý±:ò8¹/1n$vă€î‰ÁUz‚€MÍ!” SHBšÝŸG¶Ÿ¦£E÷ì©êV©¬eGNW6dH°i:Uó£#i§xA]uñ%ª~ÕÐt×$¼´\9¤†ÕªªR—_n®ý‚sBÓˆS¬¸ÆÑ3B ¶©_?UàKÊ똨ÓäÝg™ÖõDN&)?¨0(Š/2‰Eâ§SfŒ(öë®»ÎLí'™Æ’Ñkãâƒ$z4s#°mv R%õõo²%ÛɈ¢>…5q²!#Ã9IæÉöÇÒC G3Q8¤ÈÁÉ…üÔ’%K¼"Äþã3s¿‚lާ¹˜üòË/»FÖ&ÙÎ|.|ñ2²‚Œì±Ä" „{bñ•ÞA Ép‹¦sLäè’$¿ž d!?ë‡é·6MÿÔ UGkDZ+}åŠ ™ë4TŸê—lœ.À$„»"éþ<ŽÂ^I7ø”èküQ¿(ÏÓYÖÊ—º6]²>ö8™ÇŽÀ+³êBç=ã E„»˜ ‚@ê"ðæ›ošèo´Øƒ %[Üi²30$d(ˆŒQÈwõêÕª–HònQQÿ~ô¿)DŠQpó’K.ñ7I³ŽओNJ³ÝÝðÌ3Ï)œâAÆÙ…hý3]ì Lë—êôÚ—:ÊëCמÏ'=c†ú]§u[«p]éñYÚñæ´ùr¿Uë³ZC»=’–®m'sA ·!€ÔD—.]LÇËu&“-øDÑ¢e-*5·a&×›¼ µr›– ŠBG³»Q£F¦Wà%jÜÚ=÷Ü“†œ&ºy‘éÓ§Ûfs·Pqß¾}MÑÍpÛ?èçÆ»îºK¡ÂÔWˆdè›WªTÉZEJ ãø*Uª¨™º»ßˆÌG—‹ZµjGýMÜóL¤§pãõŸ#Öuœd²`h¢?øàƒ^&ÛDzeËDÂÅ$Å—%Â=Å?`¹YÝt])5ò‘GÕ9ùÿŽ”3ùóÍwß©{\Mu±»Y=úüójØøñªmƒê±ûîU'i]e±ø!àK¥×ÖõêůséIH!Ƨ^|ñÅÀ+Ú¸q£bB›Â¥b‚@2!@:EPùu R—š5kÖTè¤ó¼¸}ûvµhÑ"è…´~Z„·Fö íÇ¡6RGÑ衯×AVnÆCÚ<òÈ# RC†† Ó¦M3í)¸I‘PœWœ“Hv¢Ó‘°¡¤9‘O!Tê¦=Z1BmÙ²Åìsÿ ?¿`Áã c;,7VHÎPC< ý‘^™]‚z˜Žúbå µyÑB/Š›Æ}‡5d{“ÕÕ»³g™ñÏÒ¢í1Æýôè1f9Yþà l¯Z¾¼Z2f´óâÑ£BôûŸ3ÖÈï„sAO²½àÙùTS-B¦Àyù Y—l¯w‹N¡Ö/m»×¼¡¶/[ªíÖÍ8yèwÈØq ";ÈæêÿÈv~ÓÐP¿£EsSÌ7¨­»í?ydû¥:jŽÿ¥/W¯RoN¢šé4mŒˆü›[¶Rûi¡13õg@¶8Põ»ë¨i…~)GöH,>,\¹Jýpà€×™D·{PÈ‚ "{y/$ÊtïÞ½!Ñ·è]g¦cšÊA N… ’k•uv"D°%Ý}üö£Á¾MKù5í’'K—.5Ä®»Ý.S¨“m·H0ýíݺukÛÌ̉ÄFeðàÁ&ª=d§³Bzݺu ißÓ F讳Ñ‘g–¿9ÁqÒ3"ÜÛ¶mrcƒìGû=’qýuêÔ1‘ömŽd ’%€<[ô”>Š)¢pPXóÛm1¾=ÿA–/_>Õ§OŸù·“u!×ëÿ¼‚Ž—m9 ‰pÏYŸ—Œ6p¤x†ß¸Æb;wîT¯j€´ Ärˆ´Éaüõ¿£²Çèq1A@bE`¸&’þMMÔÅœ¬ÙH`¢±³uNeöÚ¹iUN§«»†¶;‘ñdÚ ßò¾N«½Ê)^¾€¿Xê# „{êƹú ÷D\Å iVü@@xNDÊ“ÿk_Ò.ypÜåóMÞIF&$%h‘jÑ#aÊd5÷µ×ÓD#§BT/ìÔ¡C<ëúÁ¿ÚåT8eáÂÂ}©N]ýIÿa¥J^®J-j–!@­!ï2rÊTÕ±Ic/M÷²bÅÔ¾·×Ù&éš#2OGHcôcIS·z´ÙÑæÆÁéÞW§òúHx—lg?äÔ»µ^(Ö4 u¶”ÉeÛ™ÆIòg€.®gÉv;$Ïÿùš¶èUõÚYñá¶íŠ‚–¥´©ßˆlûäÞÿÝ_öêkLÔ<ë…4N–ð¶û™ÿü˯ÞêŸò–ý ß}wÌd;ÇòYÚŒ/"éƒÎý˜¾î—§M7힟8IõÒ²5§êóó ä÷Ÿ^uh|”T'+B,¾aðªS‡Y7b‚€ ¹Kjg䊋ég<¦x2Ò.LÙm8МgÊNƒ7"‚Ÿ©jÕªÙ99w6" „{6‚/§N<¤ô {F”;z^é14¹lÑôÂ(rA:¢Xj!` ®J4ÜS볕«² ë®(©˜F?þ—)t‰î:‘¼k6lPüñ‡7Œ:R§i·{Õ¼_ð¶QLÒFÃSàÒÖ:w!o­ÙèvÖ‰j/Y¢¸!uYG6†ˆêJenP7\y•ŽFÖ/=:¢=#÷´t޵ó ÐQÔíjÈüŒÓÿå­°u›·ì.\|ÄAàncÒÝéAdí)'úvëløûÉŽu"ü‰Þ²üge4ö—Ñø$:?ˆpÇYT<ôâ ‹*&×öhgÿ'Ú©‚Öý¢U«Ôëk׺»—׎ Ûok¸/ÜÆÍÛ?ñvýCý#ì玳d—ލÃDä:N™†U«yÅYm'tÖÇ7ßíW×i‡ÅVóžq†Ý%ó8 0yÁÂ{‹ÈÉÄTéBA@8" „{Á”®’7‹4¢Ý:r/Vû\§d»Fª–î."©±ì’9–äJ+“«¬F‚‚•©§ŽþEc{¡ÖqìùÔ`Sä’ñ@º¿©‰x+/SK˼@F~ÿㆄ~sÃF£»\Ëü#©´DÒ»‘àÜ«j ÷º]ºª÷tÊ*öŽx…°gÂè³£Ž2îÞ¾}X¹ÓÐ÷çc‡pÇ`¾f!«?êôÝ ;Y§7G³ âùØc“·ÄÐZê%’uôHw„‘à 爠_þ ™¹d©Âùñ‹ÖÀO¯1¢Íb5ÏúÖÞu—]Œ8ÿñàߟûU—¤Õ)}ÿã-Š ãÿ"$qˆÄË<®œÌi:Ë NåJ™ïTzA@A@ˆB¸Ç Jé(Ù25Ù?¡ì_h„{¨¦[öŒHÎ*ÉŽÙOÏMœè ³v¥JêB­}é7t´›×®­ÊèÛ¢•*{»¿ñ†G¸CŒ6×)‘ôÀ&/X`wˆnK¶ÖÐ:Ýþa"7Ì™­–è¾æ/_at´Ñî¶qûäÈ—4ßš:UqúévWÄùŸ­k‘/o^#ÿñ½³`€6g´cRuÿqÇë]Úìo£^p%Üí½±Yó¤óÅ‹V%´î'ü©] ™îö®·»ÌïàŸþémºR뿦';bô̙ޱ,\}é¥ ©# æbôÿÆ»ïª7Ú¾«F<Ü_Gßßf¶ËŸŒ!€S Í}kr\Ùý2( ZCËwmÚ´I=ôÐCªW¯^Š ‚€ $!Ü °tŒò.± @Gz^ê‚{ Ýúý÷ß«3µn¬졸ÈZ09"î¤Ñ‹ ‚€  HòÁ£ÇhÉŒïLSˆÕ:u {XaauéE)=¾_“á®!+c ÷™K—ªáõUÓtÁnk‘ä"ªÝx£Ö¿Ñ4= #ÍW¿óŽÖ‹_à&ÝöÙçz¬£½—¶Ïpób… y»ˆFž0ø)o]”ú\ÀŠd_~½ÇÛMñÚ ;æÁüúôõÈvŠ¡¾¤ jùu¹cyfŠ¥;.Úã0Ú²c‡ÙUHëÝÆbT^¼x±é‹ÊÒ®=÷ÜsjŽ< 2*ÞWÔÅð¸ŽN8!¨IØm`C¿TÒ®_¿¾*©¥IJ÷ÿYœ4Y‡»œIÈé\å¦H*×AñÂ;FtôÚâ§´w‹‚²~ÅÅ%Œæ:Q«D¦•Ž Fd{õ›n2ËöÏb}¾wõï2VîškTÅn0Ëyt¡¨:úwˆ©Ç Ajð¨Ñfûº÷7™y,Š.ì5{çÃÍou 9»N×6Á.»¨˜úöÎ<Ф ÃÅîn‚»;î\ƒîîîî‚{pw;Ü wwwøÿþ:7ÙÍn²‘M6»UÏ3™î™Öw6+ÕÕUyGø/G0Ô\ºÀ®Ú¼áBþû5Iåä{׃vÏøŒïDGþã‰{n#FP|a”`,¯Þ¼1¾,yXÑk ÷câû”9…;‚ÿÂÝŠó-b'†æ× üÕ^/™Å÷0«U¥dE‹Ñç/_¤ßñS=éoá×%à° jš`Q§ x`aæ1%/^¬n§ïñpSÆÂ˜`L€ X—+Ü­Ë×f[‡Ò;—ð§©W6jƒÍ,~DêîOž<¡œâËü÷ïßµ"ç–ÂO­_ w.>|8=ûϺI_?àW}°~¡ ”çZÖäVòýû÷§ &l…Fa(±± €cæÌ™Ô AyÍdCÿ]üôéåÍ›—^Yjuöï߯%Mž1ޱcÇR·nÝLÞ7u}b1 ÁƒÓ°aèoß¾¦Šó5+Ðûp×”V膛dLÀÎË›O)ÜŠÏu¡^k©@Y¯¹Â⯩à‹Â@ÏÅâZ¥ ­Û¹K6=lÆLª"Ü$EÆ z™)ŒÚ,/ån’ðºX"‚£ê}%ŽØu;F ¥˜O?žqÎ[H`׿ÿª5¨Ò°ª‹…5¹˜#ÀזּmÛüÞ?~¼É÷GäÃsfL€ 0&`M¾Z³7nÛfÀbÝ”²ÝÔ½Ä;sÊvSåõ×F(?û„e¡æ X0¨$|ÞblþɱÕ_¯Ì7WþÞ½{f•íæê_ÇBA@–ùš²]«7}útêÕ«—´þ×®ñÙzôÿ¦Öë™[fL ,hS¿mس‡öþgÑ‚t?Kõãb÷•&ÕJ—>¹}”áÚõz•*R7ñ™ùM,n?ýo÷L¹‹(–/¯ü|”VÑba¹ZÛvR1KW|^ ðõ­‰q Ê„Âçú{ÒoÒDÙÇï¿ýNÕÊ”–íNìÛ‡Š5p•÷áæäú»äRª$Å‹[úâ†K MÙ æ’ Ȳ¶ö †Ïœåï°VO™,-ü-ø_øP/Z¿ŒÛBîL¸*X­ ¬¿5ÁŽìâ³Tàc?y’$tG!às ÏtHÇ”)M|»(†NŸaÐ,Å+-JˆTq)UŠŠ £Ì ±òÖ¨IC:u¤œÂû½Giבi¼›ÏÂJצMé…ð ½é¿ZÿXxzüüaA*EÒ$tS|·šµ|¹R¶'KœØd¼­>Ÿý& –Š’ØeÀÂÌÀîâ b§’&•ELü~baL€ 0&À¬O€îÖgl“= pNaÍ´[l[‡5¸_RTü˜ƒ›™­ÂŸì;áÖR¹%~°õéÓÇ 8šY³f¥ÄâW„èÎ;ҥ͇ ÊÁÊü‹Øz)R$ƒëÈÀZëÆÊvX¦Vy°¶¿!¶ºt¡ ]ºtÔ®];Z.~Â|@ 9„•a§NRˆ wÆ7nÜýH7®Ïù€Ð+Üû-\À*si&À–”©+…²º`ºtí?å5‚^ŽðCÁ[¢@~Z6a¼If±„0\Á¬nÍ4AÀLS®K`E ?ïšµññYŽÃ”  býʆ –R ¨1#8+ô…;¤Hž<4X({O&¿ÇÌK8Œ â-sçØ¬µ$\˜àðOÌ75U‹žÂýQª¶ikªy“êÕLÞóëbo±k ¥Ø¹9"RJ6j,Óú?N‰)?ï“ÄÎ@àõÚîóšÑ— hzöÐ!äÒº y £5Ûw0ÙÄ0ᯕ óçû2È@|‚>ã'˜¬-JZÂ1L²±ä"vA¬ßåóþwîÜr‘Æ’º\Æñ`‡òÀÕÄñ»kÒ$Ÿ…XuƒL€ 0&À˜€Uøì)¶Jóܨ­€rzýúõÒ¢{Þ¼y~JrM }[(Z·nígyíæ¡C‡ ¬èaQ+ò³ÂGéæÍ›iÍš5têÔ)zúô)͘1C*ᵺ¦jJÙŽûc„_Z-ðòÆ%9|¸Ã ÎÈãz@¬–a ×7X4hšW‚1bþ¦ôuòäIé¦GU° Q¾|y*%¬Ê´1Â<‚±„šr¬¹'&`oà_ýâæM4gØPiÍkn~ðÑ>äHÚ"\Ëø¨ÎXIëêRÅ\“Ôº^=ò±ErfÊd²Lá§w\¯ž´T¸0vs3¸C¡„¯làEûÒë/ÜXºDZXk×´s„ðá©}CW:²r%1±p¬•³Çs‚¸qh×w“~³a¥Þ¯M±¨2Á— KXüS»–\H‰+\°K–tiiïâEtvÃzŠ#^wÖ,äœ^¿N>[cw2è¯c¼æú´n%»w÷ð Þ ez[a a*ê_â;'!-_f’›5æbm®Úº¾~û¦¦ÖHøÆgaæ 0ªÞP FPðßΘ`L€ „ ¶pÎ6Û ïñÄÖrK?Ø“‹cÑÅJKäóçÏÅP?–‰‘P¬C1Ž §¥ÅV{X°ÃúÞ”@Ùm¬_¸p!¹ºzo}×׉*¶XCIž_“kذ¡þ–ŸiX«'K–Ì×XÁ óNA_;wî¤û÷ïKKÿ""0ž±¯ÞàìÛòMÀÐÂ× }â+L€ øEïÙÍkÕÍÕéŽW+·Ä{:ØÉ'¢ ŠLKäׯÿ©b¨ßÀÌg¡V¨jéR„nHî ëúGOŸ‰ «1ÈI#O'¬ãÍ ¬Ù KcÏ…[¸¡1¥Ä…‹š bAá½Ø…vAø$¤“‹jðU(XM ”€þ)¡xõK°Xðëša@w¿Êëï½={FŸ PúäZ“å++êk<˜v4Àïþç/_…ßûtÒUñÂ…Öàí}{µ¤Ÿg(­áý•«âõô@No}Õ{vK øOŸ¿ˆÞ .–pGÇææ¨ ‹)“ûõ£I"– ^ÇG¤ˆ(u²ääì”T+FÇϧKÂÒ_¼V±ëbT·®ÒÑÝG ;° ƒñ# /KÐèÝÉDÖÊ5Ê” Zƒ\Ûn .¨à¶R“”âó GZ–ÏL€ 0&À˜@`…{@vÔ.€T/ëÖ­#X®gË–2 Ÿ 2d.`pvrr¢âÅ‹Ó#ñã®hòˆíì¦ä Ø:ýMgÝW7¦”íúº¸¿eËZ¹r¥þ²M¥“Šàj8XBž€>hª9EIÈŠ{dL ¬€‹™”â³ G`e¹Øý¥Iy±k©õ8\»àŒ˜²¦6nÊu(ßY À½ Žà(½ódÍ"SmÚ¼ìß›ºl×ðyè×ëÙmÍjƒ¾šÖ¨.ó‘…E¶ éåaP€3A"pãî]¹È¢5‚Xæ½´2|vLˆ]…@©zÁÞðâ}…… 0&À˜9¬p9Ö×SöìÙ¥¯õ%K–¨¹Ãêý¨0‡C/‰ÄV(Ü[µjE…Tsw4zÁvIK¤[·n6­p·d\Æ:ôî¬p·cn• 0Ó yüü¹ z ?êz…{‡F MWâ«L ” |ßåVnÙªF‘MÄÏÉ! )X¬GAyõb*˜²þ>§—ü´_ºtI¨Zµ*•+WNå9Á˜pðŒÏ ˆïÇÂB‹ûO-òÒïìÙ³©E‹þºIe;ó… "³\õ w¸¨Éœ9³E$3 ·Æ>l-ªÈ…ìžÀ¯ÿýRsä © '˜/ß¼¡dEŠRŒ9©^—.2@)º-S¸0!8' °Eð%þþãG5´f5k¨4'‚Ÿ oØ N?>ÏŸOå9Á4ø=5dÈ-+ãaMœ8Qå9Á˜pK—.•Šö4iÒЬY³aÊÿŒ1Bõ5jT7nœÊs‚ 0&àÈô;Ø™Ï=ô °Â=ôŸ€ Ø,K—.ɱ=xü„V=Þf³ã ®Ý;%p°X—Àѳg  `L€ „=ûÄH,!G`Ôì9!×÷(عRÒ¡Cúüù³ênðàÁr×­ºÀ &À˜`L Ô °Â=Ô€ Ø.”)SÊÁ%L Èe» ÂÈÖ­ß.üÒþ’-8;;QŽìA „¡Ø}ÕÏŸ¿Ð¥Ë×éÇ÷ïv?Wž `LÀÀíÚã'ÏUðNø‡Ž/¶=LÍfçðI|V¾zõF/z´¨5jd•ç„mxòô9ýøñÓßøSÁ5ê­[·ÒÆUsØ}kìË]Ýä`LÀÁ °K„òôYáÊ€»g¶Ln€ … 榕˧ÚòP=¶HQÓ …ûWY¿\Ù¢4uò @·Å™`L€ Øyn+¨E«>jJS& ¤–-ê©<'‚Ÿ@¥*ÍiËÖ½²áßÿ®\ÞMñãÇ þޏÅ`!)Kºìu=D|ìnaÝ®—éÓ§‡Hßú>9͘°eìRÆ–ŸŽcí7Çš.Ï– 0&`Hàׯÿ© " `L€ 0&ð7÷UŠEáG¼NíŠ*ωà'ðìÙ Ú¾ã€j¸Lé¿YÙ®hpbÔ¨QtóæMÂÕÕ•þþûo•ç`L€ 0&`;Xán;Ï‚G˜@(Я€ó–³PxÜ%`L€ Ø$/¯tüø95¶š5ÊSôèÑTžÁO`é² b×ÝOÕp£†ÕUšŽMà–\3&À˜€c˜7¥€fMjä9ü.öPƈ*W*¡òœpl;v$¸”Ñdذa” A-Ëg&À˜`LÀưvÉÆ‡ 0%À+à!Ë›{cL€ 0Û'ð]·^²t½hʔɨpáÜ*ωà'pþ¼]¸pE5\»VE >¼ÊsÂq lذ6oÞ¬dÍš•Ú´i£òœ`L€ 0Óx»i.|5d°Â=d8s/L€ Ø(¶p·ÑÃÃbL€ 0P#°iózþü¥ê¿Y“ZÄ?Z«$ôÖíè ‘+»“± è0ÖèçÏŸ©S§NjÔø?D TÔeaL€ 0ßôu¾ïò&rXár¬¹'&ÀlœMµñÄÃcL€ 0!à6ß'X*Ü­5t­"ý:j'?~ü eË7ªé§N‚òåË®òœp\ǧ;wî(7¦‚ ª<'˜`L€ 0Û$À wÛ|.<*&ÀBûpèÜ%`L€ ØGžÒŽ՘ʕ-J‰ÅWyN?í;гg/TÃxC±päÄ7hܸq AŒ1 §ªœ`L€ 0“xwžI,|1„°Â=„@s7L€ ؽ;ŒŽ?mïñˆ˜`L d ¸/XMúÏÇfMk…ì°·…‹ÖªY㻈kƒª*Ï Ç%о}{úúõ«0bÄŠ7®Ês‚ 0&À|`—2¾™ð•Ð!À ÷Ðáν2&`Œ?ŒÙÂÝ  0&ÀB>Ý®QýÇ›*”/¦òœ<•«6S¿ãéúõÛ¼~ý–à3_“bEóSÒ¤‰´,Ÿ”€‡‡mß¾]Í>GŽÔ²eK•ç`L€ 0&`ÛXánÛχGǘ€ è-øÐ [¸[67͘`6OàÀÁãtëÖ=5ÎF «ÑŸþ©òœ<'OžÓˆ‘Ó)m†T puš=g½yóŽV¬ÜDß¾}S 7jÈÁR M|úô‰ºté¢fï§3fÌ 6 QH8Á˜0K@oTÇ¿ïÍbâ!@à胻`L€ Ø$ý‡1ÈÈ6ù˜xP:_¾|¥å+6Ò™³—èöíûôáãGrNáD)¨b…”5kz]iN†$ ¼ÈcÝvº|ù=yúœbÅŒN|˜F5ª—£X±bûp?~F‡Ÿ”íÆŠJ–(¤úؽç0½zõVæ ÊM ÆS÷üJ\»v‹Î÷’EþüóªêRƯâaúÞ§OŸéÔ©‹ôâå+Š!ݾqˆzvo:²‘^Ýܽ-/1¼žkÕ¬`##³aÄÊ;A:wJ‰’æ£*Uÿ¡GžÚž…ŸV®\I»wïVerçÎMÍš5SyNœÀñãÇ¥%¹¦lOž<¹´fÇ.,f4iÒ„  óæ 4ˆjÖ¬I?~üð³£”)SRÆŒ gggµcëýû÷Ô­[7ª\¹²ŸíðM&À˜°o†K®ö=Wž`LÀ€M5ÀÁ%°}Ç5²ìÙ3ÒÚ5³Tà´xñâPÝ:•å‘K¸iÛ~€,{Vøx‡r7GŽLª.'‚—@ç®Ãèû÷ï²Ñ8qbÑV¡´Í•+‹A'E‹ä#­Úô¥9s—Ë{[¶î%¸3©T±¤AY΄k×n«ÎÊ”.LåÊUyGM`éÌO5ýÚµ*:ôN "Q¸kݺï/‰Å×.ñÙN |øð`Á¬ ¤r TFàΞžžT¸paùY «õeË–Q•*U|56~üxêܹ3-\¸¶nÝJ­[·¦¹sçú*§]ððð0iiCžýû÷˶Ο?OÛ¶m£åË—Sݺuµª|fL „ æ³7„‡ÈÝÙ1V¸ÛñÃå©1&à7_îâÇ °5ZPLŒ+·PèâG¸)iÞ¬¶ô½­¹„8zìL°*Ü?|øH°º|,,èá‹8Iâ„Ü‘"E45œ@]»{÷¡ëINII— æ| škJÃk×oSÜ8±E€¹l2¦¾,”WXˆ¸yë%O–XX§¥ Ôøáfßþ£ªé-Ý|)ÛÕM‘˜2i P²ï>íËË›6ï1«p¿%ÆvûÎ}zö쥰º‹LI“$’œÍ=w}?¡•þúõ+ý{ä4}üø‰R§bÓ¤0û: è?þBX@z!|­çÏ—=Àî5Pÿø‰stÿþc±Û ?%Nœ€Þ½û †=zT•6•€û›‹¯Ê@¸H'JŸ2dH qÜ»÷`UÿÇ¿‹ÇÉ„Ïàd¾Ü ˜êׂú?a®]ãëzëvÜk*vͰ/ÀüèG äÙ3½c*ïh¸5[#0xð`zøð¡VË–-ÅçJ.•çDÀ ôêÕK*ÛãĉCÛ·o§œ9sšl$f̘4þ|‚eúÚµkÉÍÍ  ‚h'5YÞÜEü+VŒ6oÞ,­ßß½{Gîîî¬p7Œ¯3+0þo¥n¸Y&à/V¸û‹ˆ 0&`¯|[¸ÛëLy^a™ÀÏŸ¿Ôð¡¨EPGSŠè?ÿü“V,›*ð½‘åÓ§K%ÏeÊ5¤]»Ët½ºUhÉ"Ÿ`lªa‘@PÖn=FÈK… 妃ûVÊ4\ÔÀµ”ÌP^êÊJ¸³>´EŒAÞÊW°*8q^_Œ–,]/\„ò¹]ÛFê>¾5ƒ&O]`.-ŠüW*› $ÕËÂEÔ¤Ywy©KçæÔ¢yªçÚÉÀB<ÚµmHãÇö•¾1·yn+Åje+vŒу7ª¡oÞßô !“TŒ1wn¿ƒ˜Ám ¦.\ì!ë=|èÛ=ÄŠ•›h¼xZ SÕH$‹Û7W›ê/‡xÚ˜ûÐÁ]äsذq}ýêø;1¦MLº Á.ò›øoª´EåµX±bH<úW¯Þ¢æ-{ѱcg \ð¤NB°hLmZ»ê‹ËtŠT…¥‚™¯Ÿ®Ò¶í¨uÛ~ôøñ3y?}º”äuå¦Lkô¯ÍÝ;—(@ú‹@Ä+Wm1xÍ þÿ*V(NcFõòRhMœ±û¡O¿±4ß}5½~ýÖà^ÏÝ»¶ –-ê‚öK`þ'ŒÛH &K—mPUt¹@ÓŠ)Uˆ&P…;׬šnñâL€Äl†ÀåË—iòäÉjÛ¡x7§lךƢ6= pÇ{ðêÕ«©K—.Úí“$IBEŠ¡M›6Ñ•+WT— 3&À˜€ý`…»ý{ÑWa~0½/ÝÏ*|“ 0&`Œ-ÜmÙeƒ}çY†@·.ÿ(²¡<Ï•·2eÈ\Š:tD›6ï.2Þ›mÚ¥JiÒÜf@I +ycb]³J‡Ò¼fò²Èü«”²=§ð¿Ï zúè$<¶ö祥A`×¥ËÖËü8¡Þ¸~®Õ8ü˜ãŽŠJ¨zÝ{ŽTÊvXs®Šõ»·Óá« Öø,2+Y^¼x¥êé«VÃù£T¾"ðåÅsÛ©Fuïñ£¬”¡l‡OjŒûÞíÉcõL‚U5_ÊaYn©Àÿ7®šdËšAKê å+vhRÕ¥Œ°jA—÷¬Èµ 'ͧ·oßiECõ Ÿô§~}ÛÑù3[éÄÑõÔ¾]#µÓ; †Ÿ*ǘSÄОøð©qÏ™5B^_¶d²ºvóæ]jа³T¶ã=¹WÏÖtúÄ&ºuý ¹»U®\fÍ^JýŒWõŒ=z’Ï ¾¯ëÔ®$-â;ul&ûÓïhп6³fI/›0h‚R¶ÇŽS,rô¦SÇ7Ê Å°‚×b#@1?{î2ã®…U}¥l‡5û¢ãéÎÍC´o÷r¹è‚µë¶Óè1³ êÇÿ„AƒdÜæ¯R¥ðZsmà¢òœ>–þèÇ‚èæ nÂ…·Â.øFÀ-Ù"¥K—J¿ßÚØòåË'yjy>ŽÀ¹sçdEA%ŠÅtèÐúõëGµjÕ²¸Ž©‚ׯ_——L•… 0&À“[¸;æsçY3& èW¿ÄÒà „$)’ÒîK¨rÕ ¬Pa}‹cÚôER!›;wªR©µnUŸôV‘P Cy=sÖ9lMù¬Ÿ”ÖšT©\J(è£Éì¾ýÇ´ËÒ%Êßç‘ù¸qc‹íÙ™éÎÝJ…€fMkÜÑ@®\¹%Ïø“$IE;®Á Ú}Áj$)põe¸f!E\ZD‰IÅBÁDaI×5¦dîì‘2p¬v Ò5[µ¬ðžŽ–/¢òI’$$(Kµ ³×oÜQ÷üK»$qr šRlÛöýªK´µtñ$Å7úöi'üÚ_¢uëw á+½|¹bªNh%ðþ9yâ÷@%Jdé"ãºpÑ{+=^/ÚB‹¶x€ûÅ‹ gg'$•ÀõŒvþ¼1Ôеšº—c1õèÖR-ž¨‚"—4µjV ·¹£ î‰ôâßkþö5Á‚G«–õµ,nˆÜŸ²æð^Ô9pð„º‡ÄA‘G`\ú=rhRœ:9%nòÊÏì¾€,®„z÷j#ÓÁù?!´àüËïÙû¯*Y©b Š?®Ês"øXò‹L+–MQ‹:Á×;·d‹`Iݽ{w54¼?Θ1ƒ¿*"KÀ»fYž*UªÀ5„ZÇŽ£¼Þg̘1-qU&À‚JÀ’ÏÞ öÁõ™€9lánŽ _gLÀî °…»Ý?b»™ ”Ï·® ©“üc ,®áëºwß1”|©«P¡‚íÔ†Fhüa¬)ölhˆ<& Àj½m›†òxþü¥ð}LZ¦"0¤^a …lµ­éÀÞ”/Ÿ÷wXÃeË%á÷ EµÛ¨y3oEâ aÝ ËZHüøq¨t©ÂªÏÕË‚yBŽ?G©Ó¾Ô3ÜpäB0.7#=¯ªjøßû÷ßS*¯O@q~ÿþ#é6 Y(bõ’>}J}V¥¡t׸$Mê£|× è±Æ‹oZSg½¿n(|áâEÛ`ª¼×Ò§OE8ôòèÑSº&|•{yÝ‹'ûh÷žÃúÛ6‘ÆëIo­® *a‚xZÒÀõŽºèGÂó’ÏkÂIìr0÷šˆ#ºjåü/•Ö'Z4¯«ÜÛè¯û—F°]ýÿÊ#X0þOðLοL³ç,7ÛÌ•«7Õ=-h¬ºð_ÿ3pkd,Áõ?aÜ®¹<>Ý®V·á7¼l™"*ω%ÐYVÆ{<‹c¸xñ"M›6MM6^¼x4dÈ•çDà è1âNÆÒ‹-êoQ|¯kš²-¸ð€b¾Þ5ïøAY¸pnêÛ»-•*飨×ú4wÆÿ”ÉšÔ&-”4–H#_ò•1‚¯k¿ÿ¸M~Œ”ãwî<¤¬Y½]ðøêDw®;4÷#x¯kM^¾|M yбâéyMY”‰Fš0a¡,N›ÆYú¶¯X¾¸\ðÀ3³wÑ£'|¥Ã÷¿×1¦DïÉÔ}¿® 2™¬ŠÄˆ²dIG)“Q~±kêÕk¶V÷µÆÿíÛw-KQ£FViÿ¡ñ?¡–Šñé]Où7^¾pæ¾cäÉ“•–,šfÞ>s®aL`Ñ¢EtèÐ!u¹P¡Bäêêªòœ”)}v¾ÔˆïÂ… ÷0ØíT²dI“Ù³gTªߌ-šØYå¿€q=Î3&¼ðŠ… Ø›T¸7lØp°0&p—/_x%­aüaV~ú¸rÚW¯Ý"‘„@É[¿^?-P+W*©îx}¿zõV*ÕQß;peqZ¿a§T”ò½Lé¿…Eµ·(³d1­8†«‰®Â5´‹:;vLݤâíwè4X.`œþ þ×R¥JF—/{[4¯^1R _ÖaA 4+U²ò5Ž;5õó¹`^{÷UÓ+T0—J·hÕG)Û uάd¬Dv”÷¦4iR(.`¼x¡÷…º€D`w,!‚¦lþ/Z´`U¯VÖ@zøðI“#uxÊ”N2`+ Àz e¦‹i¿þç½è”?_é»?$ÿ'^½z#Þ v©¡! rêÔ>üÕ NS wÅÞ¸nn ÜÛÀ¸¡%ðöí[êÑ£‡êïÓ§OWñJÔ Nš@¤H‘¤¥9\Ëœ>}:@í´jÕŠŽ=*­ÓÍ)ÜcÇŽ-\ð™^ìõ¯3Ô}ñâ…ôÿî_YÜõê•*†º,L€ 0&v„ s²°Ã“Gʘ@"`ì·ÙÔá04ªÈ—7›š,ªý³r~ûö½*¥-,ØõÒ¤q •]¶b#­2°n¯¦î!¿ÕC†N‘ÇÐaSÕÖwüŸdÎœŽºuý‡ÎŸÙªE°®?uÊÛß !3XpkrìøY-éë Ëz·ù+åW9¶ £Gö¤?þð¶Y€ùI“çû9¬; Ÿû> wø¾‡à=èÈÑ3ª®ÛÜQ¾”í¸ùêõUÆžú×ĉ“ç%SóÅBöš@là¸Ñ.VjÖ(o lǽW&\iuÒ¥õ±¬Ü´yvÙà¼s×!*V².•(UŸjÖn+,)½_Kúù[ûbé² Âÿ›W³&µUšÖ!`ü;'¶nr'ìæ`qðëýôéS5áöíÛ‹Åî,*ωà! ¹sÙ±c‡paçeQ£xO1KŒŠdoÜðq¿—6mZƒ{œaLÀ4½Qñg¯é|• X‡+ܭÕ[eL Ðc¸üšƒ )õ²õ];Ó•+>Aõ8žnsGûò“Ý»gjÙº,~äÈi*YÚÇr^kÃÉ)‘òóEþ¶íûÉËs·v;LK•,L׮ݖcÞºmáˆ+†z¾PBØYúQÇk}ÌØÙò0ž$*¶lt3¹+À¸l@ò.UJI«céÂ; ®]»F:u;©²Éï'èŠo(äáWÿãÇ"ØuTZ¼x±2tž‘ø´‚E€åË—Ó† ÈÃÃʔ)C“'OAÒ½]Íh%·nÝJ­[·Aê½wqÍ™3Çfvùicä3°UÆ¿ñmuœ<.û'ÀîöÿŒy†L€ ˜!`üaì( ÍààË6L CûÆtéÂN©¤1÷:…U{Ëõ¤_uX¬›“ RSž@ÑVâÄ hÙ’Iäììdд¦tÂEi<}bµo׈¢D‰lPrõX=“úô6­ˆ÷U!„/€ùÜÙ#epO0EMcÁµÖ­ù.*[¦ˆñmú§yš:y jk|3K–t´w÷2:{j‹k!ãra)?x`'ù:Ö³Ò¿&0—þýÚÓ½+ vhsŒ!¼|½9äAI’$Ô.Û‹Gð«]¡¼oëF¼Þ»àš×êÓËük¯ëSÇ7 ·C®&_×p³sû"yßxàÖþŸÀ"Ár¿AŒ%wnŸ÷í:ŸƒŸ€ðEˆ Яo»àoœ[´i¯_¿¦^½z©1baRY¬K UªTÂ%Û9±›Äû³Šî¼yóÊ]ùò壌3Š÷è(Ô¶m[©l/-[¶ŒR¤HaÕAy®Y«ïܹS!¤±âIËúܹsSôèÑ©B… b¡ýž\TîÙ³'•(QªcâÆ™@X$ЧOruu¥† R£FÔÿM<==©I“&Ô´iSu4kÖÌbWSZ;|f!N(œ M<Ó ×aLÀfÀ' ¾@ÆWlg¤q͘1C~ ­Y£­\îã:HÚPå›7ïRêt>>gN&–64D ðEàÍ›wtãÆº-\¯Ào{‚qÉ)i"ñƒ-¥øÕWySòäw‘>Üq ßÙ3G˜*fpíãÇOµÆº{ï!!xi’Ä …EVJBà?ÿJ¾çÏ_Jå#”ø¦_Gà6ä‚ðY×+©…{ce½©z¶t \<=¯Ñé3Åæh”9SZ±¨\¹ñk¬`tá‚ݹûâ êtéœ ÜüÀ¢V±[ WÎÌä䔨¯æÂÄ=¼&¾ÿ!b*kCã¿ÿAº?yôø%n‹ðš‹5Šq1«äïŠgáuå†dŽ€¦x–Pºk‚çÎ_ÿ äŽý=­ ^×ø¬Áë:Z´(”F(㓊ÿWãE­¼þlÿ‰%K×QÃÆ>÷Ç÷— jú~9mØ5á윔­T­ƒ7ÔZÍ”¥ ]öº.-©ÍÚ„¥ò¬Y³Ô»uëFcÇŽUyNX—Üø 6LZ”Ãu ‚¼ëïÝ-Z´ XÄLjCK¥ÝÜܨyóæ2%~Ö¬A[¨Ä`Y?dÈzøð¡êGŸÈŸ?¿´~‡ž%à°‚ß²¤Aƒ¼®aó„zøðáç¤I“¨cÇŽªË•˜€>‰ïú‘Yán).ÂÂV¸[þ´ ´L“ÞÇŠJG(Y˜€=0~ÝŸ<¶r . `öO ¸ˆ °ÿÀ19Qî|xï¨Ýìž°ÿ§Ç3´Eþ)ÜOŸ>-v•å!¸È‚$NœX?¿"-«mq>ö>¦ÏŸ?‹EÜ téÒ%±¨_¸K+-ÚMÉ XXEðÔ[·nÉïË©S§1qRQ̘1CbvÛ+ÜíöѪ‰]¿~]íQ-Hàÿ ÿwqâı 4a" îª*WbL€ ØíÇ6l÷faöH–ð‘"E —/߈ ýÔóçÏÁÊvEƒLÀ¾ `‰¦lÇL«T.ÅÊvû~ä<»P&€ï™”ªÿ¾9aÂV¶‡âs‰1¢t+×2¶ Øí„E… ¶…!ñ˜@˜!€Å© Б#G4æJ•*±²=@ĸp` °÷À’ãzL€ „yƵ,Ùâæ'ÍpHƒ‡N¦±³‚;îÝçó¥tü˜¾Ƀ'Í‘Àü« ¦Ý¬)K5Â&ÌæÍ›G'NœP­Âw­Zü§€p‚ 0&D7p ©àN¸XáÎ/&À–€ÞâÌ£tX@­Ú-S¦ U«VMå9Á˜`ÁKqØà*Æ?aëvÿñýà&À ÷à&Êí9(mk×®Mß¾}s¸¹‡õ ÿúÅ>ÜÃú3äñ3&À˜€ßæ/ðq'#F4ªêRÆï |— 0@xþü¹ë˜ðáÃÓÔ©SÕWbL€ 0Ë ø§LÇûqݺu-oK2` À ÷`€ÈM86ÇÓÉ“'iãÆŽ " ÎÞ·…;¿%†ÁÇÈCfL€ 03Ž;K—/_Wwë×s¡«<'˜>=zô ×¯_«»wïN©S§VyN0&À˜€u (už<æãUU®\™bÅŠeιU&`†k—Ì€áËLÀR ,Eµ³¥õ¸\è0š.»” ý§Â#`L€ 0à"0Á*ƒ¦š6®iç `ÁCÀÓÓ“.\¨Kž<9õéÓGå9Á˜`Ö%à—•{“&M¬Û9·ÎLøÃÄ5¾Ä˜€…>}úD«W{oÕÞ±c=yò„$H`amÛ/öåË9È;PælemÀáçÏÞóÓªuï9’†˜¦eù̘€ƒøôé3áý v¬D¼ðæ OÝþ§‰Eå«Woª‰Â²½a“®*܉Ÿ?Ñ«Wo(z´(ôWø¿‚»yn Ø$›7ïÊq!0ª~çääÉ“)bĈ69f`LÀ Ô©S‡:wîL_¿~5˜^„ ©téÒ×8ÃB‚+ÜC‚2÷a·<<<èýû÷r~H¶téRêÚÕz?fCäÕ«We—ïÞ} K—®…t÷!Þ߃  `ŽIàÉ“çŽ9qžµCøòåkˆ|–?{öÂ!xò$™€žÀÍ›>‹[*T ¸/ëC¢ƒÊi.\˜ ´baL€ Ø*˜1cÊ÷^Í R§««+ýþûïZ–ÏL İÂ=ÄPsGöHÀØ òö¤pÏ!ƒ|lÉÓP™Jö· ý铇´~åõÒ,U¾9§N¯òœ`LÀ1Ì:Š~ýúIUkU¢$I;Ƥy–vO`ÍŠõôøá9OüÐlÚª¡Uý·oÛ¼‹n‹útiœ©† [’Ùý Œ'( L›½ŒÞ¼}§hDˆ¦L™¢òa9qþüyª]»¶œÂæÍ› Á%pÁ+Ô¤I“R¼xñ‚«Yndžð3¶¡‡á@CiÔ¨‘ò@ M›ÝÉh$øÒXáÒĹ?»!p÷î]Ú·oŸÁ|ðÅâôéÓ”3gNƒëa5óçŸÊ¡§Ï˜…ÚwV§avÜçÏ3P¸Wp©G%ËU5[žo0&`ŸæÏK¿¾ý¤Zõ«QþBæ.ÙçìyVöHàÖ;4uü,5µŠ.e©ïn*oÄ­›w¤Â=s†Ô4´_ktÁm2›#0uÖƒ1õêÕ‹œ ®qÆ7J•*Ñ;wäâDûöí}à+až?ã0ÿÃäÊ–-+]üb‡$oÞ¼„€ª,L 4pÐÔРÎ}ÚE‹øjÔ&elõ®]ç³íà ©¶÷LxDL€ 0&t+—x4R»A5ƒ}j²mÿ.¢ÞÎ;ÉËË‹¾ÿî_qƒû­–aeé36˜g˜@dÊ”‰räÈAU«V¥èÑ£¡%®Ê‚F€îAãǵ”€9ëv »•ÑHØöÙø +$lûyñè˜`LÀ›À˯¨®KSZ¿f3}ùòÕËÊ%kUŸk5깨<'˜:ÇOžÓ ‘Ó j×®AÞÞ3… ’ ysæÌ¡×¯_SåÊ•)FŒ”'O*R¤ÅWúN¾~ýºBØWxOÂÿíŽ;ªkÇŽ“×´?pý8|øpJ˜0¡ ®Zºti™NŸ>=uíÚÕ¤‚:mÚ´²½uëÖI…8Ê%Nœ˜° ²d‰¿}líСƒôõŒÀ­åÊ•“cǸ‘>zô¨6 _gÄì£FJY²d¡R¥JÉvÐ\ ™RœC)y'J”H¶פiÒ¤‘õÊ”)C2dí7ΗËÒ ÔÕ–ZÝ€° è3Öúà3n°r×,݃»mn XJ€ƒ¦ZJŠË1ÿ|úôÉWäkc8°’€F‚ ŒoqÞ†üúŸ¡…;»”±¡‡ÃCaL€ 0³~þüIG—G´èéJ T»AuŠ?ÁÂ]“¿‹¤D‰jY>3& ºôEï?|2hÉQ¿ó¿}û– 0†e{¼xñ({öìtöìYúøñ#8q‚òåËG·oߦhÑ¢ðò/óòåKªY³&A qrr¢äÉ“KKpXªã¸qã†üMö×_™l~œW­ZåëÞ½{÷¨Zµjtúôiy/bĈ”5kVÙ,Üñ;nÏž=ò\¼xqƒúX`À"Á—/_äuøíÇ‚ÀùóçéÑ£G4fÌ9æ5kÖÈ1Tþ/³téR‚_iøš†%. €.]ºDPlwïÞ]¶Ý¯_?SU)0uƒÂ2(¬LN€/Ú$¼ñÿðáƒOL ›h•"E úã?hÿþý¨e»Eá 𬝰Ýgdjd¬p7E…¯1?xxxÐû÷ïý(á½Å_ˆ`UÁb»ØÂÝvŸ Œ 0&ÀÌøõÓgÁøÝÛw´Øm¹<â%ˆKPÆkÂÁR5|fÁC`ßÁã´Âc›jì¯?ÿ¤ot¢*ÛAbÀ€ÒeÃÖ­[¥e8¦¥»»»´ ‡«Í3fP¯^½¨hÑ¢JQ Kô»wïÒøñã©mÛ¶’„^qŽßPP¶'K–Œ/^L… V´–/_NmÚ´¡7R«V­hþüùêž–@™Õ«WS’$I¨K—.”1cFéb÷[´h!•‹èoîܹT·n]úS|˜þùç‚e>”òp¡%JÙ,Z·nM°‡e;ú€e:×&NœH½{÷–‹Í›7—®bäMÝ(ô5jDõêÕ£éÓ§«…(ìÑ/.&L˜ Ç)R$]M¢ÀÖ Ë€² È36˜gB•^sݺu Õ1pçþÀšQ£Fù_KØ V¸ÛÌ£à„–º‹A9V¸ÛöS5öá.{Ù²í'Æ£cL€ 0øùËG©®'òL¸ºÐäÏ¿þ¤HQ"IëI¸2`aL hà*¤m—¡$»Jî=xlpÍ‘2°ôÞ²e é-Á¡ nß¾=M›6®]»&-ÝÁïCáÇ—x´÷$(ºµk7¸sËX§®_¿ž²e˦ݒg(Èaí …8ÜÄ@ëz½@ÙŽ1ÁÒàjÁXR¥Je|IæoÞ¼)ÏðíÑ”ÏøM‡à­~Iš4iämôõãÇéG_>(ÌZ7(,ËJ?WN3&À™+ÜùéóÜD¾ú´/–VD  M›6¶T²Ø ¡|ÐK8aU˜`LÀÖ è£Z:Ö1£ÓÂÕs(^ü¸–VárL€ »ö¡Õë½ý~HºÔ)¨K»FÌÆJ`%Ëggg{Ouc1¥PÇ¢ã§OÞ‹&šŸqãzæòÆ»bÍ•Ãu[2à ,Ë °ò‹ ßcL€ 8V¸;ÒÓæ¹‰€»»{ êí +Ü…Îê•~ýÏpK¾-}A¶úäÑÁ÷oßhÏŽõªfºŒÙ(¹³·ºh"ñïôþÝy'qÒ”9[nºsë]¹tÎDió—‡@ÅJWöUà«صuÃrºìy–Þ»-~L}>‹…§3)Y‘ÒeÈê«_`ŽJàË—¯´Ñc y^ð¢ûwÐ'a±é”<©Bxr[>ƒR E! `–ø&¾û´ëf(uÚø~Ò݉å­pÉ€H—.;vŒàK}èPCö–¶ƒ ªÆ‚ïù)S¦$X}ß»wÏø¶AþÅ‹R9 ¨"™6mZy¯Äôòk!@³†G~ M ,Ë ° ÍùrßL€ 0["ºŸ¶D‚ÇÂü kD¹Œlß¾ž×±ck•pÎzÙAËŠm´SÆô§{wnÈÙ”«R‡ÆN[êçÌ>¼Gm›T¦ß¿Ërƒ„ÿS(ÜîÙBc†tó³®ñÍX±ãúR¸/œ3ÜfŒ¡W/Ÿ?uì ÌO;€J”u¡N½FPŠ”Þ?– r† 8¹3ЬÉnôòÅ+ƒY?rJæÇ˜Be*” ý;Û”bÖóÂej׬«cælCTá~é¢iJÓfHM¶â"îg|¸Ã'ðÔ¹ã(gîlÏ3L€ øO`Üwºvã®*X»Z9*Á‹~Ї52d ÷sçü6ÌX»v­T~#¸h‰%, ܽ@á®@5Ué‹0䀯ô§OŸRŸ>}høðáJáŽò{öì1«pÇî£ýû÷Ëf5%½Ì„ÒŸ ° ,«Pš*w˘°9¬p·¹G²Eð ˆ/\¦Áh–,Y¢nµjÕŠù^/p-cj»£¾ §Cž€±w¿‚'…üèl³Ç2kÒÜi#åà 4‡Õ;ñæäÀîÍJÙ+ŸRå‚ϽҬÉÃhÚ¸æºV×÷l_OçÏ£›S‚„óÙ©á㦎›EãGNõw;¶ì¡3§ÎÓÆ]+)abßÛôýmÀÎ Ô(ïJŸ?yü;xz»Ü ` S4þüòkLCÆô£Òå‹ûU„ï1&`‚À½ûhøØÙêN”È‘hüˆ*ω 06~A‹M›6¥ùóçÓ–-[èäÉ“”;wn_]¼x‘j×®-}¤¯X±Â×}s°ëxýúõ„ßo›7o¦Š+ú*ŠÝÉP¶CÊ—//Ï©S§–ÁP÷íÛG¤ºuë’Vøï››yyyÉ\Ë–¡P7(,ËJÏiSÏØ¸ ç™°Žâ}ËÓÓSý/æÊ•‹ü þl‘8f«¬pwÌçγ ÇéÕ«—ÉZøÒ¦W¸»ººRL–å‹6FÀ؇»ØjÊâ72k(…ûÇïéø¿{©P±²f+íÚºVÝËW¨$EKåµñÕë6ײfÏ‘£DS÷Я^ÙW3-Úõ&¸¹y'Ü×?¼‡ŽŠcãšEk£ÏžPû¦.Ré®ù³Tq‚ Ø9#(ÛK•+Fm;·  ™ÓÑ»·ïé_qÿßGÉcÅFùÿòüé jÞ mܽ’øÿÅ6_šÕ½£k×¥5hRÛ¿b|Ÿ 0:õIŸ>QwõnK‰ÅWyNž@̘1éÎ;tôèQjРÁu üªÃ•IÁ‚©råÊ´qãF*[¶,A‰ ÷2¸å-,ßÛ¶m+•í"в–Jýúõi„ töìYªS§-[¶Œ*Uª¤ü®?~œFŒ!›Ë—/åÏïãÂlüøñEÕãÇ©P¡B².,È!X4iõîÝ[æK•*¥”õòB(ý Ë °ÂtýzÆ¡„ƒ»eG k×®4kÖ,ƒywèÐîD¬—a…»õØrËL€ Ø8c‹ šêÿƒBÛ)y*åVf×¶ufîŸ?¢Ãû·«FËU1­ô !"õ1]•³$qxÿU,}¦ì4y®i;bljGå]êÊ#c–œ4¬o;YÖKøx¿zù¼P2æPu9ÁÀþ=‡Õ43fIO³MQÿ/qâÆ¦*Õ+È#s¶LÔ¿»·¿ÜKÂÇ»—çUÊ”Õ[™ à„M°Ä½F]êÖ·£MŒ—ÁÂm;ÒºÍ{Ô°3¦KEÛ¸ª<'‚F kÖ¬Ré ëtÍBÊw(¹!Ó¦M£GÑ©S§¨jÕª-Z4éæåæÍ›Êú|ÃÈ“'Í›7ÏÒ!Y½\`Y…&åß3¶úĹ&ÀØp&”_¿…rÿÜ=`L Ô+,ðÅ’Åp+£Éþ]¥U–ן¡lÿòå³¼·3ÅKWÑßRúôñCª~¦¬¹•òP]ü/ËùðºÀYçO5.Ây&`÷NþpÍš=³Ùÿ—:®ÕÅÿKxÅãÌI¿ýç>{úœì=L{w ÇŸ¨z–&>xD;·î•õo\»%­-­Ør_„µêégi×¶½¾|Ù¶M­ÞÍë·iÿžC´~õ&:´ïˆX˜¼¯Ý öóÏŸ†A¿;(R¼š4Øø2ç™°€ÀׯߨCo+g­øô ýC=¦6{8Ã/:”ݱcÇ6ù™”4iRú÷ß©G”$I±{ñ9rD)Û]\\ÖèPnT²eË&æMš4!(Ö_¿~M¤ëׯ˱à:\λE?ÿüó\(^¼¸t)sûöm9.(ÛQ.H>LNNN–ÕÊ…ePXù÷Œ­6an˜ 0E`ܸq´sçN¹“H]äDˆ` ÷CÍ1&`kþGÿ3’ÞŠÅàg èÝʼ|ñŒÎ:B9ò2(ƒŒÞL¡¢e)j´è¾Êö¯_?UÕý»7Q¿ŸÓL®àÿùçŸ4núrzûÆ;H¤sêôª'˜€£Ð+gwïØGCö3ûÿ2mÞ8zóú­D“*MJ_ˆ°3hÞÌ…4{Ê|zñü¥Áý1£SÃæõ¨C·VfSßEå1C'Ѫ¥ëÄÿ¥w?Z# „«†–íšR½Æµ„kó±!´òÚÙSXã×si*Ü㼓—ª×©Bc¦ 5˜#”á=; ³Â?=ÜLi’"e2jÒÒ•6««]’JóƵZ©¼–ø;§·û,ÌóÜ#ÚeZ½lÍžæN7®ÞT×´D¨´kïöT°ˆ·Õ¦v=¨ç_ÿ3¯pÇ®„îÌ>ƒ öÍõ™€½3Énܺ§¦Y¿VE*RÈ·qUÀNeÊ”Q>§¥²å³¹X‰%’¾ÔýjAªG-—/_JÄÑ£G§”)SRÔ¨QMV½zõªÉëÆa1Kwȃ¤²crvv&|wôKàFSa¸‹xø{GUXÊ›øK6ÞYkªì7ÉX‚RWk+0,µºeeÉ3Öúà3`Ö!AžÁÅÕ‹/ Ü [§7nÕ˜+܉pž 0‡!ð?ñEY/áˆ-Üõ<Ì¥áV&iò”tÿŽ·ri·Jj¬pÿöõ+!`ª&å*›v'£ÝèVížçOÉjÏŸ>¦Ö +P§^#Lº‹w&àȲfÏDÎzJÏž<§Æµ[QÏþMº‹)UÎ|pM(ØÛ5ëJÇþ=i'õSÆÎ”ë3L¢¤N‰ ÊÁ ¾UãN"ˆñEƒëZæÉ£§4¸ÏH:~ô͘?Á¤Õ£VV;{]ºJ ª5SÊöúBY?lÜåå¶lØAÝÛ÷£O?iÕÔùöÍ»4 Ç0ºæuê`%õÄÑÓiò˜ª=ãv €÷ìE“Å.Ÿ"Æ·7þüÒJš, ¹¯˜I‘£DÖ.9ìùË—¯´|õ:sþ2ݾû@¸{øDÎ)’RJqT,[”²ŠÖ-ÛÐG¤ R²h~aAÃݺͧÏ^ÐÃÞÿÃ1¢G£Ò% º-}ÅÇâ½åÐïÏåXbQªd±°Ïèö4rü5µhQ#Ó¸áÝUž¡C–ðEŠß{¨~° ÇP¡Nš4iäк¡Y>(,Ë*4çË}3&`žÀóçÏéÊ•+„E9¸ÖB<ÐäÀÍv ÁMÞkà²+¬ +ÜÃúäñ3&h¾,MØ¥ŒÅ,ËV¬¥‚§îݱžz gP÷èáÝ„ ªÂG{ÑR• îë3?~|§­ë—ë/ùJÇO˜„ræ-¬®7nÙ•Ö­r§¯_¾ÈkGî"ΩÒQ¾Â%)¿8rç+BQ¢úZU•9ÁŒ@‹vMh•°Âþ*¸;Á‘*³°¼ÎO……R0oÜbJ?É í7F)Û#FŠH{µ¥‚磿„5àÑÃ'D`ÖiÒjÝS(8{wHK< }Øöí6D)ÛaÍ¥ž9éþ݇ÒÅËb·åôíÛwÚ¾iÍœ|¨ê GéÒ¥eþÕ«WÔªU+éVëË¿ÑqcÈ!Ô®];UGŸÀ.¡Aƒɘú¨(“:uj¨13&Ož&½²Â]ÿ´9͘€C0öáÎ.e,üz·2îÝJ¥ó”.CVÕ€ÞL‘’Åj¹ykK(Í{´o êšJ@a¯W¸'qJAó–ï¢vMª(w1¨wëÆy,s÷v1Køâe*Sm×Ö¬|7–¯9X=/[çFÍêµUÊiL~Óq,œ»Tº_%|©òÅ©A“:¾”ïð¿a÷®•¿þúS´7Ÿ²çÊ¢ø¥óÎKÕËÖ¾vß‹€ÉGi›Pœ—«TJ–9.,_áë)r$Z»})%JœPæ'IDù æ{ŒÂIw5¸¸Ñc«Ÿ ÷ëWnHeûëWodíº¶¤n}:È´þÏðc¥ÂׯNFp7£ ,ðóäÏI¥ V‘‹‹„¿U‡¦;N,*Q¦¨,öÛï¿kÅ©€˜ŸSò¤*æäy¥Œ‡k,lh’0qY øñùógºtñ eAkƒCŒ?¿"DŒ@ó—Ï$ŒÃÑeؘ™ÔØT áþËiŽäpF@ÌcâžØ¿Š’ˆçÅâÞ½û pš^\ܼm?m‡&Y2¦¡ö­êkY>3&À˜ófÍšE3gÎô5|·Dü Ä×puu•e"Göþ ï²ýúõ£?Ôƒò^“-[¶ÐêÕ«µ¬:ë•ïê¢HlÞ¼Y°þñã‡þ²J#®Æ ÷ZaMXáÖž— 0`#`láÎAS-GkìVfp+£)Üñ¡¹oçFÕXÙʵT:8Ùs GþÏÞY€7±5ax~Üݵw÷ân…Bq»¸sqçâîîîî^ÜÝݵHqç÷?ß »Ý¤išúÌó,Ù=¾oÚÐÌ™ýæ.mY·„Ö¯Z@ׯœ7\55lV‡¦M˜#‹V-Y«:Ü—Î_©4£–íþRíj¡8i×¥%=yüTÕ¹ýüé³®4 6 ê Göë?ÑËÝûý­ëœüè©LÊŠ9ÒgLkälWæ…½^ãš´`ÖR)9³xî êØ½RmñNuÅ M³lá*ªÛ¨¦*…ƒ9ÏÞôNð¬´uÄëoMÒTlO›7N÷=qÄ\ÁiŒ}O9Û«Š ¤>ÝZRŽlèÝû´÷À ò›A‹—o¢ŸâgÞó…U­ÓN:ÝÃj6W‚Ó=óZ}G zýŽ”1½3 íÿ7ÅŒé­ ¢Ž=†©ƒa“‰RùçBEÂ'L€ 0‡€óÖÃÃC&H~þü9Á1‹„ÃÉ•+,XÐì|÷îÝ£k×®™­Gò*¤OŸžâÅ‹g¶Ý¹sçèÙ3ÃSiñãǧüùó›m‹Š?ÒÁƒ†\»¸¸P¬XAK:ë²dZ’=çÌ™S÷ÖlØ%8w£Fõ O½@·ý¾·R å.â 6&À˜p<={öP×®]ÅS‘†„÷Ê §NRN)oÞ¼²M>¿»íÛ·†ª¶µt‡{áÂ…iРA>œãHžŒHkœçÖîpÎkåQ6nÜH9rèÿ]kiM]­ö±cÇJ %èÑêÆ “\ˆ3wî\^±¢áï~D¸ãhÒ¤ -\¸ÐÇ-`Ãb×®]² ¯á 7g‰Áæ Ò1ÇŽ3ÚÁß¹[¶l¡ºuëJ罓““lÜþa‡{p{Çx½L€ 8Œ€]WÖp÷[­¬Ì­—…ó=JžÒ™<„ó]±’e«RD‘Ý’…ê6nk©‰MuqâÆ§òUjÉn\½@Ë„´Ìýû)þˆ˜<º?Í[¹G^ó?L 4€lJåjååW/_—Ò2«—~ñG÷Øá“¤t êïÞöv¸§H•Eº–BÈ×(öäñ3‚Î9ôÞïß{¨ûIöD»1€‘µw—BNeº:¾rrKÈÎ(¶Ï!ÂaÍÞ¿3þâg©=¾ `Þ ;ˆ$Ά(+ldl^¿]è‹MD½·lÿÅŠí¸¨_¿É¥!ã³(I;q•7WVÕÙnʧycwêÚg´ªÕ}üÔ‹÷Gâgù¼x*$\Øp”Ú99¥qNáë»÷î?¦³®R’Ä (“x$¶H,j‹=Oj`S …Ø4B¤¾­×Ï_¼¢ —nЇŸD¢Øòþ”/Õ¶ÌëŸm¾~ýFçÅ=y½~Kóç øâóÈ7†þ'Ï\¢ÇO<©d±”Tä„°ÅÀòíŸßñ" tãÖ½iÎÂ5Ô»k ©ù¯ŒKD¾ÃÏÆ˜`Ž%€¿/G­FN[ýôéÓT§N‚vxµjúÁ–ú+u^^^´iÓ&‚3Ö¬Y”-›·$¢Ò&´½‚i‹-Œn;|øðROÑçÐw‡ >œ‡»Qc?^$NœX²1={ö¤%JP¦L™(C† 2y+¤mV­Z%Ÿ(èܹ³Ú>8°Ã=8½[¼V&ÀJà?úÏh¼0ÿ ctÍ– ø”•Ù@Zt&Þ÷ Uk[ÄŽÚÓÇÒµËgeÏXqâQU÷Fº£`}CÆÎ¥_Bâf³!þ‡ø#QõlL 48!ƒ^¾xUÞjœ8±ueUP™9kF=y¨x´ó—h2HB?|Jj”GŒÁ•ÍJM­i|ˆ’ùW$CU,j4ó9”6–^áp„fû”±3 0Іǚ«×v5ê¦M,OD7#Q«5K˜ÈwÉ!+³uß:°÷°ØlÜ/ŸÐFóCc~úÄ9BÚf/­Û±T$kµÍÉjm¿ÿGµê»Q—^úI¨¬õ©õÚ÷|‹x?ÌI‚àKåÊãèÍÛ÷$FL N>ƒ&Òü%ëÕvJ›dâg©ûßM©UÓÚâ‘kãߥòºnÓnêÒ{=ÎaÅ"Š<Û5¦½Û‰Äâ>ÿ?Âï"®'M_b”ø5šx¢£˜Kš5iY'ó!oÔ´m?:|Üðÿ¤2grñ³:fh7Šcaã'U–ÒÆ _5y”qj5êLk6"ئŽíGíZÖSª¬¾Þ¼uŸš·ï/µó!é£Xº4)éï6¨m‹ºJ‘úª]Ó×iÇîÃÔ¦ó zöü•l³fñr¯VNmoéO2˜Ú‘ç¨r­6âÑwïš!ý:žˆ`cL€ 0ÇèÒ¥‹Œ\VF-T¨Ô G4{òäÉéñãÇRffÊ”)jt÷îÝ)I’$”/_>¥›Ñ+¤OúôécT† DÏ_¸pöîÝ+ë<==©G´cLJ%±÷1i0(H—.g»vÙx`òäÉôåËÉk;úiM¼×p¨c#†' pÀðÝÁID´ã©D¸#?¸;܃ë;ÇëfLÀÏ´äc° ùåç ÀŒde„Ž{¶œùéõ«r1bÆIË8|5îݤ1CºËq#GŽB•Ýê[Œø+^¶Šêp‡ãý»7?¡÷®ºÃÈ2 DàÞû4¬ÿ¹"DšW«YÙâïK™ò%T‡»ü}É &3]8{YŽóHDê"᨞=zøD-N–< !™',eªä"JÞ +óL8ãÄ­¶Óž`ƒ@ùl†ôŒ©32\¸p4eîX© ÿåÓš3}¡ì>°÷r)VP®UÏY“@Ô¥xA¡K?J©røkñREì½ä9yô mIf·o28&!¯ Æ4@IDAT3{êêÞ¯“CæÎ$¤lŠ–(ä±BÒ yse¡Óç®È[‚C¶bV4b`Ýèu×J%ÍÞú“§Ï©Fƒ¿éÔŸŸyÓ†Ož½ ¿{Ž Câ}^-¾æ¾ˆ.XºAFPÿ_Vµö]l@œ0—~§þ¸á=µUôVüÎÕkÖ´ò8JƒO≎m»Q¾âµhÓÊi”Gܯ։ߟ Õ[ÑnjÅ=ÕiÒj¹•7­ ë5vÊ܃©ÝºóÚuBW®Ý¦Écú˜}z`çž#ò}Ñ:ëMDzt}A$.Ö3­³=¥øÜjÓ¼Ž^3.cL€ 0?@„9dB«]»¶Œ Ö>¹ï–-[R©R¥¤CöÓ§O„ p8‘”Sϰ‰Þ¸qc½*Y¶bÅ êÝ»·<¿qãAŽã‡VË“'Å[!‚| àĉ2Iê;wNzGÛ¢E‹¤D tâU¯¾@³ÇÒ¥K©Q£F4iÒ$r@Jû üÊáœAùÝáµ1&à¿´ß°ÄLæ¾0ûï"‚÷è•QìÂÙã´b‘·¬C©òn^ü‡íh˦Ñ`ÿúõ A3Þ’}ú`ˆ`D›‰’°³Ý,® qræÉ®Þä]N9­^ë|øðI-N(¤/àl‡¥I—Z-ß´n«znz²i­÷—!8éK6•r*ž‚Ù¯žkOí;Ju\ÿ¢z"!jÛ&)|xŸq!³¤W±vîÕŽ’ ™ tçût¤Žœ5s^1«”—Wv¸—wŠ×ɘ€Ã (Q”êÀâñ%6ßPdeÐ HíØ¼J ‚«ãåd0xšt™….ra!œ@º iŒ¢ÉÿnÕƒ´ÉLµ·ÿRh>O;C-ÊW ·z^»Auõ|ãš­töôõZ9fúÒù+•KªÝÐ]=¯ænp¢`é‚Uôì©·¼†ÒhÆäyÊ)•,[Ìì—0¥¦ûr)ù×xGNáÞ3gÍ ëï ™5ôéÕâäó§ÏÔ´^;êÙéy|z×Z ÖûOå&užâ&Œš&q"Y*>M-uZgµ(A¢ê9ŸøTNÉÈcó|ŠÇøËáuñ„Á”YËȵN;Š›² *]W:Êõœïˆߺó \ $\Žy,§u\)…ˆ|.æ’W$Æíe$£²b÷&“é]AwýøÞT¡lQJ)ž iÚ¨íÚ0‡0.ì·ø™AT·bçE.€K×ËËŒbÃêôÁ5„HüäbœBrÒÒ¹£©U“Z²Îë S)]iÆÜ•„û„E…öo[(×™9cZÊ$~Ú·ªO‡v.‘oµStë;FFócºùÓ‡Ñð)göLä”2)5®_M2R6J¦ÍYAo„“žÝOÉÔ®^nžÛNË祩ãúSþ?‰õÚ›–]¼rÓ´H÷ú€Ø˜ÌQ¨ºx"e }ŸlL€ 0&à7tåÊuV­Z©çæN Ûž5kV逅¶·¶¿¹>æÊ!Q¢ØÓ§O•ÓPùzäÈzýúµÙ{‡¦:68`òI˜0¡Ù¶Ô3½¿‰µíðó¡ñãÇS‚ ¤Fÿ€hݺurn$TÅÓHî C’Uô næý-"¸­œ×˘ð#ÓÿXRÆ> å*ÕôÑñü…Kú(wD"!ÆL[®>‘ðÚë%U+••:·ªE‹çL [VÓÊÅ3hhßöTÁ%-={òPN)RdjØüoG,Ç`Á†~_¦Ì«þ¾x‰De W¥6"‚|îôE´uãNZ"œäý»¡by* Ó´u#õ>ó ç{Õ?NóB£¾[3)‘‚d«p´/ž»œÜ+6z›‡5$\*Tñ–”ªXµå+hpàc 5E[è®?4׮ܠ>]ÑñÃ'å|Dô°vnu:'%ËU¿¢z@¯ô꥗l‰§–úë¥öêÕy4žiþèÁcZ³|5­ÛV®2dJGX·Ö$Œ¯^Ž6™¶ ‰˜[ ‰— ºäW¹ž=užZ5êH[6ì O!ÝûZ»b£L<« PÄdl¥œ_KŽé{—öÈèî\Ù3ÒÿL†G„ôñS©×€ñä$4Ë·ýq®+Íà¸V¬[Ç&ÒÙ­\+¯}DêUJ“[åR2ª9§,tÚ£ˆ¨r­!Yjvñ¤†b÷52L“g.•NxÔ éßч¤ʇýÓ‰Âü ˜6g¹ê†¼bMÄY6ÍJy®™¨ÞŸ(y¥Ì¿_<|J·í•ÓdÍ”–Õ«êcJg!9ÕR³‘§»ž!²}ÁŒaõφ…^seØ\¹ÿÀ[òÊ\;¥ü_‘ÿeìä”!w%‚?`L€ ØOàÒ%ï'Ó¦MK °:X´hѤÓuûöí„£téÒVû˜kmxÅâĉ£œ†ÊW8Ûõÿâ…ñv€q÷î]ªY³¦ÈéôS²)[¶¬F±bÅRËà¼×Ú«W¯¤NþСÞoÚzå1gΜ‘R?ˆ`7 „„LP… (wnïà{ŸpPæ Œ×p1)Ïɘ L?ØYRƾw¥|•š4wÚH£Îe+¹[N5êàË‹.¥¨Ç€q4Jhóbãïåžíëä¡7¤&ÎY'5æõ까 „d…‹ þC{Òà¾#Õß—›w‹'RôHø}™µxå4I˜øÏ°žôÂóAgý›Ðˆ>`¬.¶¬92Óȉƒ}Ôš4„Z4ì@wnÞ¥§BǽKÛÞ>Ú  ŸXk–luëô ÿÞ‹ GóáÃG‘£á=õë6X¬²lZ p^™Xt¢~ÁçÄLEÃÔ’‰èã+gøÈåáR¼x‚æl¾Ï!ÂK8L±™JhÄÕWnV Áîíûä!›üSÅ­‚ÔÏ7)æK"#F4Ý„ž¯¼ÞÐþC'iï´cÏa‚–¹boß} êõ;ÒÁ‹©@¾ì²ø†Hì©XÁ|9”S£×¸qcѺe†Ÿ1£ “‹lYô5Ok’ój½^¾zKAGEBO=K&dT„õ½p"?xôŒ²G¶v݈7gåK»Ð’•›ÍU;¼\ÁŸ"yb³÷;V uî‹f´Öá”ü'/„ÚØÆ“‹bcÏç3(Ö;ÿþƒ´k³Þƒ[0&À˜€)DM+†hõ€4$IÕ:—áð-vðàA©ö¬q"õ={ö8T®\™Ò¤I#uòoÞ¼)uòüø!ñ$Mš”Æç•v³dÚ´i„'ð6Uöïß/uߵƌ#“áÂɮح[†¿w0ô÷GŒ!“âBÃQó›,Š“!Ánp3v¸·wŒ×˘€ õâwûÐ*²2ÜU(_ÅðÈ»Zà' šv¤BEËÒìÉÃEbÂ>vÆ1%¢Ú]ÝQý¦(uZÛxþ°\’ *&­P‘dsêøY"‰ðvýßáȪQÛ•þjÙ€Ò¦÷ÖlWWè,¯Ø´€æÍX,ÖˆT×Ñš×£ŽÝZëê‹ÃA½uïj&õëWmr._´Ý 0ÿˆˆtÓ(s£F:ˆBï%6ß%Û%¢i7¯ßN®Õ+ÊëŽÝۈϊüÔ·ë`ºyÝû *# ½ïºÜ©m§¤f—Å?]„N<ôá±9ÇLûEæu(~‚x4mÂlº|Áðø­lôçç4NT·qMj&žàM]-™€;/~nk 4t¼!/3‰Aº‰Kû ™D[æËúÛB~H±´šÄ»J™o^éÓ>ÚŸ!¥›Ç×o$aP†¤­¶Ø[±É„/§Úu§L‘Äl×T)“™­óŠ«-t$|ÅaͰ¢gÓ;ëÛTvñ²mr2ÚÁòäÌLë–N’rBÚr>gL€ 0ß@âMÅ’'O®œ:äÿ^¿~Ýh,üŸúæØp`$ÞT Ñí®®®Êeˆ0a‚d w£mABY=‹7.-[¶Œôž@äyΜ9 Zø_¾|¡åË—ë ¡–½|ù’&OžLmÛ¶¥ôé OøÁA¯8ÝÑÉlq˜³±cÇÊ sõAµœîAõáu1&àï|JÊ„ñ÷9Cê;v©m½¿F-:¿šsš 4ròbê3d²ˆ¸COß'è¶ÇKˆ'MAÎi2R´èÞQs~û3àLIL'Î%£²Š$†=¥WB·=¾pX'IšXäGp¦è"2Ø’ÁQؼmcy@ºr0¿~þ¢LY2yZ3HÕ Ý‹Èð‡÷ÑuÑ=z4J•&¥\ƒž#zî^ûtfkçª×¸á0gyòç¢]G6Ò§ŸéƵ›ôB$L&4¹¡±-zTsÝD4{,É Ü^‹HéŸÿþ¤Ø"ºYkå+—&àùTHò¼xþ’b‹~I„î¶6q¬¶Ÿ;žÀA¡»}ö¼áç$^ÜØºÒ%˜5‡xzbÞ´¡òqéÅ+ ‘Þˆ€Ç†J¸pa ’IŠE·ð³¡´qÔ+þ&Ñj¡æë æ6 Í™oàÏÒ!ËdÎ~ÿ÷Û\•¿”kï)aü¸”,©õω$B:FÏ’‹Ï){íÂ%ó_äõÆlÒÀfL@#FЫæ2&À˜ð//ƒÜº8: '| lÍðeïÞ½U]pkíCB}óæÍéÞ½{tùòeõv Í=ztÚ¼y³Ñßh€¿Ã›4i"ÚjͪÅIŒ1¤žz¥J•èĉÚ*‚L¹råd ƇA‚mÓ¥ó~ê¯uëÖtíÚ5逇£Ýœ®~™2ed„~Á‚Æ’F“á v¸á7‡—Ƙ€ÿ0•”Ñsôøï xtGˆ3eÉžGŽ“Ça!•@̘1„¼RyøåÙ]¬¤‹]CàóÖÉ9¥<ìÀÎNp®Ãùn!Âß’%²48؇ÀM‘P³kßÑrò¨B7½~íÊ¥Í\+–$ÅáŽä¥oÞ¾'H½¤zâHÎ {ôØ“à¼×38ø•¿# =I<-áƒ# øP"Â×,ž@©SØ<¤³Srµ/´ÊÙ¯g÷î{ëØêÕ+e¿?¨”#Y«o,xÊC±2% Ñ’9£”K_¿Úº¡70$el±ÂY0itojݬŽ-͹ `L€ Ø@ÀID4_¸pA¶ôôô´¡‡c›$NœXFY#Qgh2HÆàÐ3¼HFzÿþ}!×™2fÌ(8Ô­"ß?NOž<¡C‡‰|2Ÿr0ˆ|Gt¼5sss#0@òþÁZ0¤nR¤°ýï ksF=;܃:Ïɘ@ `áÎû‰·…Á˜`LÀ× ä5h°£ãç/_éÀáSTÊB¢Ú÷ï?ªs §¢«žA<é¡8Ü·ìØOH4jj»÷¥rn-dqÜ81éÅÝ#¦MìºNŸÖIušŸÉ}Í9Ü—­ÚBß¾—s4ª[UF”¥ÎyÅY¿nÓÊg’ƒAYЊµÛ•S¯Ø\€&<ìùËW>êQpW<™âKŸ6•ÚüÔÙKr“Bïï­kBzæø)ƒ3&KÆ´”_ó~ªØy‚({­–¼¹a’ˆ —µK&QÁüúÚýæúq9`L€ X&½pÅá'­­æáá!rô|Í¡ýž!C]U]§ŽÏMÒØ±cËjDV§J•JWîP;˜¢]®-3=·¥iŸ zM~±dÉ’Q½zõü2„ŒªÏ“'áiÆ÷öŽòý0&`3ÿD=­q„»–Ÿ3&À˜>2gLCñ…ÃøÕë·rÑõ›õ ;è¦ö\H) =C-.R(·zŽÈø"lÆÜ•Ô´auJ.ä´6jÂ\õ²R¹b#éÕ†6œ4¬ëJë·xÈ–CGϤª•JR´hÆ’G3æ® ¶]†È6…„c¸™ÈAÃ:7‰Ä½°Y V‰ò”N8ðµ¶c÷!Ú)Çš3'ñ„Æ™?²<ûž¤jB*IkûÑ­;µEVÏñ¾@ç¼ÐÎGßK׫kV:úô™ªÔjK÷Dd>l÷Fo¾J¿¼Þ¸uOlPr0˜Ç¥@.Z³d%[lL€ 0&àXˆVVìôéÓôíÛ7ñdX$¥H÷õ_‘c¥}ûö²-̘1C×á1bD2Äðÿ¢î@ µåŸ?¶ÐÒPeÚÆ–hn«ƒrƒK \ˆ½3¾1&À˜€¦îìp·Œ«™`L€ QaÆ¥ ÆR¹j-è—ØP!’úfÉçJnUJS¡ü9)i’ôúÍ;é¼h¹HÚ+¢àaQD^Nm©wå^­-”‡;#Ç(R®! é×\ æ¦?I'ü>¡ù‹(´Òµ}ÕAì<ƒ»D‘|´_Dç#Ê>‰:4XÌ;GfzôäíÙwœÆMY ŽÞµCõÜU8çóæÊB§Ï]¡÷>Qá2õD‚âNT´púüù+íô8LƒGÎ }¡Ã0……Óyí¦ÝòbÖüÕô¯ÈY-ó¨Q#‹'NSïãÕùl=A4û„‘½¨xÅÆ²KËè¶p¼ã^ÄC‡Žž¡…Ë6¨Îöl™ÓQéŽÕjµ–0µC«ú4nxù¤€­÷Åí˜`LÀv..Þ„oß¾¥ 6Pݺu-€¤œpÌ+ær0JOÌÄ®Ö6´ZèQ¢D!Dx³1sØánŽ —3&â ˜&ûŸøRÈÆ˜`L€ O?¢'uî5’ ËÇ;ÈŠÙô®Â‡ Gë—Mö!_2wê`ªV·]»y—ŠD¸Zõ6í*¯ÇèE9³û”œÑmlcá¬IÕ¹1¿{ÃNº=‡ýó7Uw-cT7ú0ªZ§t^{‰Í…V4ªÿŸ¸êñw3=ižQ¹rѺYm?u!=~úœ~ˆÈ™óWÉC©Ï‘5¥Ɇ•Hz¥ÜÚk1—¼4¸o4bš|OFMœG8L-Uʤ´míLùx¹i_®/\Ö×o,t÷Á»¡åacL€ 0ÿ#9sfª^½:­_¿^N‚hu$ïŒ_ÿ©"Æ9R]PêÔ©É\Oµ‘'Z‰šïBªmïÞ½2Á§ÞPXÓŽ;Ô*HÕpÀžŠƒOt°wI 1&:øˆp'|ecL€ 0&À‚+ŽmÒÕÓ[¨Aí*ÖÌF:¢Ú[7­Mo r¥½£î”{N+}ž9´†Úµ¨KÑ£EQŠÕ×,B&eϦ¹ÔVÔ;Ú0÷ÙÃk©cëºsÃéM‚>ÝZù˜:K¦´töÐZªåV^FîkD‹…fOLZ××#ñëþm ©T±FåaD’c”íÝ2ßlY£:ý{¶¡ƒ;SV±FÓ¿¶àøÆýóXNÉ’&Òéí·¢‹:w§Ièèžeìl÷Zî͘°™@=T™G‘»»;áÕÔàøîß¿?;wN­êÓ§zîÈl$I’Dk„ä©!I:ê´k*SÆxÓÛ´_3ŽpçŸ&ÀB-S w½$^¡ß8`L€ 0`JºíK挢)cúÒ{èþÃ'ôü…—ÐçŽG)„{Æô©)FŒhï.²pÊOן¦ŒíGwÅ—®Þ¢Ñ£R:ᇦ»¹¨¶ûW<,Ž‹ÊÕ‹'XlÇ÷¤Ñ}hâ¨Þtïþc976 Ò¦NIΩ’[ì+V Zµh¨|˜`þN ~ýúy˜Ž;ÒË—/éË—/tèÐ!yèMîææFÿüó^•ÃÊ *Dݺu£ñãÇËlܼySz“`c`„ NÈÒ±1KXRÆ®cL Dðáp7óèyˆ†À7ǘ`L€ 0J@‘“4к¥“hÄ .ÄÎöúfóm1&,(P@j¡Ã™ž8qbÝ5Ã)¿dÉéØFrRÿ¶öíÛÓ®]»¨D‰f§Š=:õîÝ[Fà§J•Êl;®` Þ’QHð+`¡Žv¯µfîñpm>gL€ 0&À˜05}Z'Ú°|Š”Õ «æU2&ÀB6¸qãJg:îòÕ«WR²ÅÓÓ“Ò¤ICHd+–AÆÌ…æÍ›Gdd,X@ïß¿§ÈçÉ“''''')y>|xGNÉc…pìpáo0ß`æ ˜F¸sÄ“yV\Ø`L€ 0àFšý§ö¯¶ªÙÜî‹×˘)âÇïoíö0Š3&eÏž]öôç>L@!Àw…¿2&êü÷ŸI„;±†{¨û!àfL€ 0&ÀB,zµ*Ƚ!ÁŸµˆÌYO˜€$àåå%_Ÿ?N.\`*(¬,4á*&Àì Àw; q& %’eHÄvüÈ>ªT4½ö¶CÄùûwÞI´pCµ+ç#$ccL tøùï¿9JdêÚ®EŒ!tÝ<ß-p×^¯4Â/ N½-ZŸEóJ™@("0kÖ,ÂÁƘhìphâ<_ˆ#9sfé¤ýõëEŽ™ ýR Ñ:°woßÈ#¤Ü—¹ûxòè¾¹*.gL „øúå+á`cL€ 0&`+OŸ Á)¶¶çvL€ 0&Àì!ðíÛ7zöì™=]¹ 1bP´hÑLJÉwÇ3åC$öغu«Ì´íîîNÐ )†ÍXúŒYɽ~Ër[ê}Ù¿“îݦ^wë7š"FЬ^ó `¡ƒÀˆ:Ñïß¿¨ië†ääœ2tÜ4ß%p0ÕK×Ó•K×<*Ç‚6xñbÓÓg/(jÔ¨.µÚï¯.4øúõ+ýøñƒ"FŒH‘"E M·îë{UXùº#wß¿—ólß¾’&M s†ôIðÿõüùó©aÆþz«üW¿âåÁC òåËŽfмŠsšôT·qÛv{ôþík#‡{­­(JTÿßé q ù†˜@0'0zpWúýã•©P’ ºä æwÃËgCàä±3ìpô}úDË–-#òòò2ª]¸p!¥L™Ò¨Œ/|O`çδuëVºzõªQç P³fÍŒÊø‚ h ?ìAׯœ—™¨áTÿ‘•úÚå3Úf´zél ÖP÷¿ÿ^Ѿ’[}NPbDŠ/˜`L€ 0&À˜`L€ 0&Àô°Ã]ŠeS§N¥Þ½{ëöüüù³n9ÚNàþýûT©R%2HÆ… ¶} n* |ýò™ÆïeõÞ‡ömç£M¦¬¹¨Z­¿|”s`L€ 0&À˜`L€ 0&À˜0%Àú ¦Dì¼öôô´³'w³…ÀË—/uí¶ôå6L HÉŠ+v\»@¸ÕnbW?îĘ`L€ 0&À˜`L€  cÇŽQ† ä1kÖ¬€™”gafp„»0¾-îÔ©=þœöîÝK¯_¿ömwno…@þüùå7n¤ëׯ[iÍÕLÀ˜@øðá©bµº´|ÁTã +W"F¤ŠUëZie¾úÁ½[tãêó tj"FŒD%ʺÊHá¼÷FžçÎW„â'L,Ï_½ð¤³§Ëó˜±âPÁ"¥åy@þsëúeÚ½}=yxž=}(74’§LMiÓg¦ ®u(b¤H¹ž‹ „:Gž ·oßÙußQ¢D¦’e‹éöýüé3yì:@G§çÏ^Ò·oß(…SrJ•:%åÊ›ƒ ºäÓí‡Â{wе+7ÌÖ£"vìX”>SZŠßw› «—­§ÅóVȱ7¯Gª”¥{ Ÿƒ(,\´ÅŽKÖ›ûçì©óäùì…¬N0>å+˜[mjmíáÂ…Ÿo©%[ò}ìÜêA?þTÇ7= 6¬+uÚT,Yf ‡¯™`L€ 0&àKy;v¬üÛ]ÿý÷__ŽÀÍ™€c °ÃÝAKmfëÚ£DB™³füZuhJæœïÝÚ÷¡O­K ‘Ÿ&3uèÖš*U-§®‡O˜`L€ 0&À¬øðრ|4i=xðÀznÁˆKÊhž† 0À%%{rN›ÑW‹pcív#^/<ŸRóºe}8Û‘€VkˆÀ‡S~êØÚâ@;oèV„jWÊ'çžOm<1ª4¬ÿjÑ ƒg»éšOŸ8Gu\ÿ¢k¶˜VùêÚëÕkÚ´nU(Zƒ.¿b±/êg;ýˆ¸,ûòù Á¨Á¨q­Vôö}O(ëGük·¨]Ó.Ô¯Û`úï¿ÿ”*~5!ðÌó¥I _2&À˜`¡•À´iÓ(Ož<”#Gêܹ3;ÛCëB¾oŽpÂoŽo–öãÇúúõ+ÅŒÓ7Ýìn‹]ĨQ£‰¶Ç¾ÿN#~üøöt·Úããˆ#†Õ¶¶6ÀzñE8 Ûº.ng;ªî iˆ>6uH”$9p)eS[[!бFÝæV›Fæý3[¶R Ê’ÝEš$iJ«}ý»Aõ…ÄÄc9MÔhÑ©»pЕ,WM<GDâŸ!Hàìܲšnß08ÏfMF²ä 2ªû÷Òx|&ê@R%K¶L>îûÐþcôèá÷4q’DTª\1mbÇ5~r§K›Þ´eõ]¡¢ù©jÊ2š=yʤôøáSòعŸ¦ŒI?~’íºwèOI’%1’eQ'#F >ƒ|>áóáÃ'™‰öî: ›{>{N=:ö§‡Ö›}"r2ŠÕnPC9õ·WÓµùò•îÞ¾O·oÞ•œî°Ã‚u¥âî´a×rJ˜8Ùõ”«TŠ ÉoTÿóç/ñyú‚6®ÛJ¯^xɺ¥ VQáb…\N£¶|Aäùüuê9‚V/žÀ8˜`L€ 0&@^^^ò`L ¨`‡{¿3W¯^¥;v^µQLÑ¢E£bÅŠQùòå)zôèrU·nÝ¢íÛ·Ó¥K—Œ†–,Y’5jD»ví’Ò5W®\‘»yLHž<9eÏžråÊE:t xñâY½ÃgÏžÉyNž<éCçªfÍšT©’AÆâÂ… 4eÊB"Š›7oŠ/Ó)_¾|4lØ0rqq±8Ö¶mÛ6š?>>}šd÷9rd‚OÕªU©yóæäììlqK•à1sæLû÷ïK '''¹ã™;wnj׮Ў5v2Xšü“'O&Ü7Æ|üØàÀˆ$´©Á¸J•*Ô°aCJ‘"…¥a¬Ö}þü™¶lÙB7nÜ êÕ«S¶lÙ¬öáö¨ìÖ€&Žìkô»gn¤j"ºÝœT€¹>–Ê#FŠLý‡O³ÔÄG]3!ËTìó§tþôQu9#&.Îöªêu¶œù G½¿ÚSÊùéá}ƒôæՋØá®Râ&à83Ñ3DJ+÷4éièØôš©e‹Ñ:ÛáÐ>~€Ñ†ºs'jÙ¾‰pÞ§ªejK©DÅê3‚¶í_«Ž¥= !<5nQ_[dt¾bñêÝy ,C„7$]0¾©}ûú6­Ý&‹£G ÎhKkÿî½Ü صm¯\Ó³§ž4mâ<ª¯éÒÕë¼r›eÑ©g[ªRª–Ô¿G‡ifÈ=ª‹ &'KWn¦MÛö‘×ë·ÏdÃ(˜Ü/“ 0&À˜p D¶Ã_¥µÍ›7ËÀKmŸ3À"À÷$dªxä Èô Ä´lÙ’M òæÍ+£ÀMÛÞ¾}›ÐöÔ©S¦UÒ) ÇðÖ­[¥3òÚµkûh§-€“ÿòe}ØôéÓSÁ‚å#:K–,1rTâ>:$õÔ»wïN£Gëë¶¢MãÆuñATþµk×ä1räHjРM:ÕW‘éØ(׳g½5f•û{ 4¼p Ù*tõgÏž­n (mL_áD€£}РA"’ï£iµ|ÿ0'ŽÁƒË‚Q£F6M|k_¾|!l `w†9‡J}ûšÿâîÛ9¸½7„‰“ʨuDb[³j5,꿉߱ëWÏÓ»7^”=wA‚F¼=vþÌ1£?8×õ,zŒ˜ä*ž$˜2Æàä»xî„^3?•Ý¿{“ž>ºOïÞ¾¦8ñ’µ&OiÿfébÁìæõKôPèÝÇŽ_nD˜&‘Å&äÍkEäð]%ìDiD²ÙÈ‘£˜.ÅèÚ¿ïÛh2¾°ñ=¸ïHõ^K”)J£& V¯MOà³×€®RúuW/]—QÚ‰’$4mjõºn£š4nø‚´ ìü™‹º÷[÷¨QõUªW¤H‘7!sÌX1iÖâÉR‚gÎô…rí+ÅæA»N-,F¹Ë†:ÿD•š¶ndÄôû÷ò æ¡¶há²ôCü}¶|õVêØ¦a¨åÀ7Θ`L€  h‡Ö8@¯^½Òñ94amæP81¢¤Í9ÛçÏŸ—§ˆÐ†„‰ž!Â\ÏÙnÚ4uêÔ¡Zµj© MÛ ºúÎ;¦Åê5œåY²d¡Å‹9ÛÕNÆŒC;wî4-–Ñï%J”Ðu¶›6Æ8õ±SyñâEÓj³×ºÎvÓˆä¯\¹²ÜÔÐ>] mf˜¿[·nºÎvm[œCÊgúôé2Òÿýû÷¦ÕV¯—/_®:Û•ÆØLQ<*eüê8p[³|‹S²©¬5ó÷zèŽgIVEVßœ³ «¥üc4Ì;4s£¢9Q¥¢hÅ¢é¾J¶ýýû—QŒ®µHP;tÜ7ìa¶iÍ"už1Cº’ÊÖª˜—j”ÍI]Zצ&µJRþL±Ôä¹øìA"ÝBYâÊv]ÛÔ¡ºU PùB© ÷§g¾¹o½þ\ÆÌ€LÌ•‹×ÔjsQójqRͽ2eÍ‘™2eÉ +—¼ûkÛÙrîäì-•õô‰§n—U˼?kjÕwÓm…íº´Pÿ?~üKóg-±{©4ðñB$„eó&pZ$¾&ä|`p¼³1&À˜`L€ 0 N€îø¥M›– ãlÎ2eÊDýû÷—ÕY³f¥V­ZQœ8qÌ5WË!ËR¨P!rss£œ9súÐ@]³f 2Dm¯=ûÀ)iÒ¤ÚbõNtÈ¿h ‘ÜåÊ•S¥o”:h¦k Îó~ýúEÅ¢èX¯«««ŒîÖöÁ96P‡'ì1ÈÆ/^\F²'K–ÌÇsæÌ‘NrÓŠß¿Sݺue´½icãÒ9"D0­¦ëׯK'½ +‰%òѺööjãûŒ |(-ôÄ#G‰ê£\[P­ö_ÚË`w¾këéÌ…Œéæ ¤^†õë@Cú´#$ë³Õ2dÊaôs9yt?Z0s,}×yb'qÒIå/~grˆèz'çtêtÛ6.Wϵ''…Óüû·²(Qâd”Wl~À¦Dýº6£{·¯ËkÓàЇÞ·Ê8Ž`öB$dmì^œ®]>§ +_ŠM„Ås&4í±I€sHôhíµ×Kêß­¹Ü˜Ð–û÷}kçâóÐGàÒ…«êM§MŸšÔšE‹•¶ì]MÛ®“Géò%¬u1[ÿøÑµ.ŽŽLÈã‡ODnˆ“²Mº iÄçHБ\‹;¹Ö¨¨®ÿêeýÏ&µ…-4‹Çvù; Ƙª…šM—ó⩊‹—o„˜{ãaL€ 0&À˜™Øá@ïë™3g(þü>\ÐG4÷Ë—/¥®;tÁƒ9"®Á­gp\oذAFÂ=z”Ö¯_OçΓÎ_8‡µ™’Çk‹Ôó=zГ'O¤ÍÂ… ÕrÓèYC-pÄãõ„ J-sD+ýylh-qâÄ´víZz÷îa½›6m’Îu8Ö!¥£µGÑ_ý¥-²z½ú•+WÒ›7ohÿþýRVò:xjšëZÃ=CšGk  ½{½#dQ×¶m[©Yèõ}ûöI†8Ç£JÐo×Úܹsem™µóŠ+R™2eÔM’¸qãÒ?ÿXÖÛµ6&×[&ÙŽ²•ÜÍ6B2Ð2k˜­··âçÏiûƳ'õG}3ç£w©gdžôõËg©Aß¼]/Z³ã í:v—†Ÿ¯JʬZ2“&6lðÙ2~‚DI¨zfjÓïßѸa=É%{jÛ¸ -™7IM–ª629qÓldlß´Ò¤Öp¹C$]U¬rõò¡>oú(¥˜5¿ãÈmò8õ¦ÌÛ@é2d•upléÝV>“)[nšº`“<"DŒ¨ö8j–,=u™Zæ(fHûE8Òû žD»ß£—Œ~Ö ³³mÃrªàZ›Vm;%×?qöZ™t‹Adë¢YãÕuÙsßjg>a6@PÅ2eͨœÈëŽ-{D$÷Ku.8üMmÍ ïhæZõƒ^òå4éœÕ%?zà½y ÚpòíÛwZ¾hÚÉW£Çð½D:@;Á“„+×í0º+­Þ¨‚/˜`L€ 0&À˜@!`>Ü:ˆ,0$,céÒ¥4aÂr2õë×'D[#zÚœÁÉ$IÕH4Šä«ÐX75”! '¹Ç—Õˆr…óØœV;!\/âuHÆ Y™â"r\1Dß#r^/z~âĉ}vÅ K‡¬X±”"õã@·c#¬u z¬ÑþÖ Î|l6è­1HÔŠ¨yEöÚé­[·VìpÌ#ù«ÖðÞ ‘«©!i*ÜâÈ!ƒ‘Þz‹-d„<ÊÚbxwïÞ-7;ˆc†Þ–®ÜÆ +)=«àZǪ–¶^?keˆïÑ¡ÅfÅËT¡Üù‹Xlc­rìÐîÇ3lˆu©êÞHí’4¹åÊW„ª•Î*#ÓW,šFÍÚt§˜±­?IƒAú ™Lß„ó{Ëú¥ê˜pìÚ·](Œ?!t)Mµ´s¹¨ípâZ£Mexêò-ׯœ§ŒYrªm =³W#U£Èÿ\Ñë?þá’>ôÞÉlÐèÙT±Z]u¾nýFÓîmkÕklŒ™æÝõ{½zNCû¶—m”d³¸°÷¾ÕÉø„ X!pçÖ=µEò>ÿÖP+í8ùõë·ÈqÓ¨'6•Þx½¥ý‡hé|ïM7D·» }v­á©³µË w<èVË; AÛ.0Ï$ˆ§Nÿôñ3ù¤÷„Ú‹ç/}°ø!tÚÁþÌÅtU±Ý´•ñf¾:A(=Ù¼}?½yk,Ù·lÕV=¤ÿ½J&ø¶™`L€ 0&°Ã=Þ¥#FÍ‚/cH p{ ÑÔzÎve&ÔFaû¨ âO? }»6ÉU¦ÍÅÈÙ®,ÉEkÖk)œ]“e<œî­;õWª-¾â÷hĤETï¯v´RDÈïÛµ‘>~0v„¼~õ‚¶nX&l Œ¶Bè6ã'LL.ÅË«ÎyD¹kîGî¢O ¹+²äÈKÎi2Èõà½R éÕKg‘{½2úå¸×Cž+M|õêHfHH«u¶c!I’¥$8ÕŸ ¹X…ªµå«öŸ,Ù½e<ž?7´C½Þ·v~>½¼^z©73vLõÜ'_¿|¥ E­G¥cã¹÷À®%j£i8NÏžzʲÒå‹SÜx¶m  âÏHxª>}ü$žXñÉqδ…„ÚeÈ”Ž6­c­Y¨ª×‹fõú-mÛuªU.ªXðÍ2&À˜`L€ á‚ÏRCÆJù )h ÛkÐi/Y²¤ÕîppW¯^]uXá Ç2"ßm5wwwZ±b…EíyÓ±àØG2VÅ9:sæÌÊ¥ÅWÈ© :‘m°“'OZlJl*T¨PÁj;DÁ׫W¦N*Û"9#Ö žà¢5Èø`\{ A¾u¸Û3÷±ŸÀÿþ÷?mÝfOn4¼Ùs0*sÔdMº÷kq8¿&j½}óŠ:~â$)èœÐp׳1c©Å7¯]RÏm=Éš3áøý{.]½t–NÝKÇyйS‡Õ'T0Ö=[¨[»º4uþFuhèã#"¶C8Ü»ô©J*A’E±ªâýQ Qíé3f£›× k,dc¦Ž@ù]JQñ~åÈSH:îá¸ó­9’™sÚŒºÓ'NwÅá®u¢+µ9þûóÙ‡:ÿ¼oen~ ÝœœSˆ¼—%ϧömZù…`â$‰hòœÑ”·@nì^¶^- Šr2X¢õÆž³]©·öZÿ¯ZÔX/Šɶ'ä¬ê_ˆ ¡]úÿ-\º‘î!áMæ{`L€ 0&À˜@%À÷~c‘XR0pzÛãÂr!cb«!ɧ6Bfß8ÜsåÊå+g;ÖuçΣååË—ÏèÚÒtéÓ¤ICЀ‡Ý½{×RsY‡5Újp®k úî(ë£ìÒ%ß;057c;*5øp¸»Õnbû¾l>|ªÛ¸­/{ù®ùÝ[WÕZ™µPçäý{o‡‘NµÅ"|†e‘è8 h÷ƒ[iˆÞôâùSÙN÷s§Ž¨ò2%JW¡X±ãÒ»·¯¥uÑäÎþÝ›eŸp"’¾BUï(OlL[´…:6sS’¾yýJ:ìá´‡aLD½7iÕÕf‰ôs$³H‘¢`H‹1’!Ú_ÛHO‚õþyßÚùù<ôH›>êpòÈð;k ûéÃ{CâßLY3"³M OÄÔiänZ,‚ÆhiEî…4”*uJÝ¿1Þ½}G»·ï•}$ŠOÅJËSùT|-Ö 2.Ž´—š'R¤LfvèÜùr8i-\¸°”Ê9%¥,À\ØŒ ,]¹…~Š'ôlûîCôÊë Å‚O>譗˘`L€ 0&ÀBv¸Âû=iÒ$zúô)-Y²DD2Eòõ L£±- ]s­Ù* £íãÛsSç‘VªÅ–± ó¢NÖ,Z´hÖš¨õpèk  °Ø±ck‹ýtþüùs?õçÎC UêôÂQœ._8%'ÄÏmMTuÀ¬Â±³@Ò@±¸ñPB!ebÍ$Lb­ A[}ùBÓ!h\¢Œ+¥H•ÆG¿è1bRåêõeÄyùÂÞõ‡÷ïTîá…„Se·úRÒl‰dáp?´;}ùüIŽY¬T%é@×NY–ÕÛOÓ1Ö~áÄ?ul?A^18ðçN)en–l8""Mmûö/fʺüúê_÷í×uqÿA@›ôóô‰s"GÃ7!eùï<Ö¾y7Ùf,œ¨ëp) ÝÏ.P›Ön’o†\îuª‘éßÔTbæËç/VçÒ¶‰ãGíñ#ÞOá¥p2ÿy[¡JjÞ¶±Õõqc‹þhø—®þýù“àïÜž¹êñá2&À˜`L€ 0À%À÷àß«W/©M™ÅÖ®]KpÌB^IC}cˆ ‡¶9’~Z²Û·o´Þµ†¢þmˆP×Úž={¨K—.Ú"³ç¸·¨õ¦c©šÓMM•SÓ¶JÄ{îܹ¥Î¼Ò¡hÑ¢vKÊø&â^™_‡’r*÷"%+R<‘ð38›“³w”iÁ"ehädï\ ~¹/8ÉÌG^/ ›I?þKÍÚö4;$¤qÒ¤ÏLwn"îß¾ñÖ‰F'ÈÊ@C†„¢Hƺs³FNF“èU6ÒüãR¢<á€}xÿŽÎœ8(ôâ—«‰IïݹA ÅZÿî9TÓËü©13?£}5޾oûVÁ½B—âÕ[zûæmX³…ê6ª©–éœ?sIu¶£>o\zÍüT¶j©·œLÍznºc!"<¾HZúêO”ùå‹×¨tùºmQˆÍµkW¼7éô¢òÍvÖ©@BØc‡¼îÅKÑiÅEö8wá]¾vÛbw軳ÃÝ""®dL€ 0&À˜$ìpð 6¤ÁƒS³fÍdT»2å‘#GdbNHÌ899)Å6½vèÐð¸vÓ¦MUýcmGèˆ×ªU‹Þ¾õ–‹€s9K–,Úfþrž5kV™`ÕËËàdÛ¹s'-X°€š4±,×hs°Ò¶Z3$Z=pà/^ÜbS¬gáÂ…jDÌeË–M^›:Éß¿OS¦LÑ}Ô]€O‚=Š®uhÔ .ôSDlV÷G9™€åäœ^  È… ']u÷Ö5!#q\¶EÂÑl9­'ù…¶ýÞ-öCûvPÓ6=t?{”(ÉOq%{¥X¾fÈ”]j®_¿r^JË ù*¤h`†Áæ‡ÖïßAW.ò,äÊçBù rX@‹¾d¹ªò7¬'-˜9Vv»øçÞ´c˜;÷Ofææ´µÜ?ïÛÖ5p»M sÖŒâ³Ï•Ö¯Ú,otƤyÒi G¶žá‰µ‘ƒÆ«U©Ó¦•qÕkGœ\¹t]8ÆoÈ¡à̇ìŒ9KŸ)­êpßµÕƒ:vk­ þG›tÔ¡Òe4P+l8yáù’út¨¶L’41¹×­¦^ó‰ß è%K5õÒÕ[t^l´äÌžÉ´Š¯™`L€ 0&À˜@ ð}†¹@]nðÎñÅ‹SŸ>}ŒnâÆT°`A2¼6j¤sñSI‡+dcŽ7DŽê =lØ0‚ŽxÏžÞr p+VL:ÉS¦L)“šÖ¯_ßbô¨26æ²4ŸÒnÔ¨Q™Sƒ„ËÊ•+ ÑößDÒÂ7oÞ5™3gíڵ˨ ºêˆ wuu¥R¥JÕ):u’<yD¯·mÛVJÜÀ9ž)S&Š?>ݼy“Ξ=+%r>þ¬t—¯¡Iž<¹Z†Hù-[¶Ð±cÇÔ2åšøyòä!p+Y²¤L¼ Ç?’®"!*Çâ=R,zôè4oÞ<åR:Þ—-[FpÂc3†÷ïGíÚµå{’$IŠ+!RïÙåË—éäÉ“Mý¼x6l jÕltÚ׬YCø‚‰9Y_½zuum|â¿ +“TH `ƒ'¸6ÖzOMjœP{¶¢‡÷ïP©òUŦB:{òp-¢'îË[M—!+,RÚ¦Û®#¾îIMOÝ'Û/š=^ʸT©Þ€ !I$}ü螈TßF—Î{Ë,”©P]D¨z;ÕÉ*U«Gc†v—úð¯_½PŠ©ªŽœL¾B%ä¦!6¤ÎŸ9F·¨A\kSμ…åÓ ¸¯Écú«c2¹§ø ÓçO†“G÷§ª5œðX›2Sdç‰_ïÛÎi¹[(#(IBê/6Ðúw*7}Þ$ÊÕ)Hô‰ód)’JgöÞ]èέ{*: Ý©T¹âêµ#NàÔ‡~;,j´(T©j9‹Ã6lV—vˆÈö㇠Ÿ9Û6í¢£‡NP¶Y(s¶ŒôúõñtÌ5ºyý¶””Q5i°ø[,†réãõËç¯rãA©øúå Ý»û¾~1l *åx…äMíü¶–‰_Ï·î´o;áˆ+Žt¸£1Ó®Ážs¿Þ·=srŸÐI þ_µ)uZgêØ²;½|þJ$0þ"•‡·ZUè!ãähÛ½}/½÷^[©ZyŠÕðš¹yð÷иiè¾[3‘Dù¡löîí{³kGûÖ›YÔzÇ ø{âÌÉs榕å"„§#ûP½Æµ,¶ãJß°ENFõõ›÷}u×2J¿2&À˜`¡”‚!Ù˜@P!À’2z'<==mI+kb®“¥6ЂGTzÞ¼yÍuѦ‘¨[·ntïÞ=³ÎvtF¤¶½öãÇím®?´Ü=JÙ³ÛþØ6¢M»víJëÖ­“‘§ÊØ/^¼°èlWÚY{E„úôéÓÍ2éÛ·¯Œ†G;{,Z´hÔ¸qc›»êmfà½ÇlCò%ÉSzË ̬þ;KëNýiáÚb#!‹‰"ŠÏ†úM:вG)Qâd>ê-@_}ƒÇ%8j–Œj7×6½Ðh:nÍX´•pÕœieeÐÆµFsM©N£64qöZµš[· ’Ÿvï?†FMYêãi…öBk¹²pÂGˆQí Ç›Öü‹™v{ÎýrßöÌÇ}B/…óÒŽƒë ÎôÄIé‚€fû’µshÂŒ‘VáºX)ÔÊÉÔnPÃJkC5ôÓwÝDGô9 bšíS¨h~Úº õü§³Ù6–*°éˆD«ˆh$íÛ+v¶["f_Ý+¯7´c·ù@½Qh’ìêÕs`L€ 0&À˜háî â;w¦—/_J%Y¨¹¡!'ƒÈé½{÷úÐW$eêÖ­k®»t&Aæ´Æ¡½v$úL—.eÈA:¹enÍé=~üxURÆZ{ÔÃQgzÍš5¥4Œ¥>žÁ.#dR%~ýúuÝæÐ>­\¹²Ü$€¦½©åÏŸ_êßoڴɇN:´ì“%KF³fÍ"K²;v,%L˜ÐtxõH߸»»S=¤î¾µ§ " 'ž‹‹ •-[V:ò!•c«!±-6´?3xêc²Zt&öÚªm§t»zóWÿÒ­S s ¹• {.J)•›×/Ñ«Ï(I2'rN“AH5DWšùúîõš“›H2ûôñqÜ5÷äçBâ¤)¥#‘Ù¶Øïÿ~«Íð¹RYÈÓX²ÒÜæ}ö䡼§˜b qÒò¾ÌõE4ýÈÉ‹åñæõ+)C;ŽÏ¤ö0«Z³±¨±¼É¶dý!sK“åØ,°ô~Ú{ß'åÊK`Úüñ4Í냶9œé°W/½„<Ôò|ö‚Ò¤sÎæ´Â¡my3ºyÛÆ„Ãûõë)Qˆ\Š”ò ¹óæ°y|.ýÕ²5hZG|><£Bçу'=zTñ„LJrrNA±ãøßÚõzåþç·^[.ó&°lÕVú÷´Ÿw©å³{ŽÐ ñóšÐL²_˽¹– _øŽyKÈm" 'NjÙ²eð½)^9`L€ 0B€îz#œœhÅŠ6–>}z©ÙmSc+=î›rÓᇾCôÚ¡©þðáC©{G96J—.-ÿ@´´èßã0gHH‹ˆzèÒ#²_Þ±ù1cFy@^ÇVƒLÎÂ… e4|¸ÑõîÝ» ‡©Ê—1cF2=2eÊDµkצeË–‘³³³/Fç¦L€ 0&À˜`L è°E¶"è߯ ,(Q¢PĈ&E¾3vïÞ]–—/_žJ–,IôÒZ¢D‰¨jÕªFè¨7.¹¹¹QŒ†äÄ=…ã]1///ruu5r¶G‹Ê”)CåÊ•£èÑ£+MéþýûT³fMÂÌÑkQ'â&À˜`D€îÞ–i5g:"%`z\½zU>ÊW§N[†ã6L€ 0&À˜`L X` ÷`ýöñ•ÌóåËGß¿WgC¾/\ß½{—:vì(ËáXß»w¯t’+ ‘ ß17nÜHîîîJ1! Îõõë×SóæÍÕòK—.©çS$dàðߺu«t¨#B~çÎò|Ïž=ªÃþùóçÔ¥KÙßÑkQÅ'L€ 0&À‰;Ü ¤n”ÖÖ¢´ãW&À˜`I€îIŸçfAœÀ›7oä wn]O»"ñÕòò˜`öø÷ÇÙ±aô¿0ÿ³oîÅB9ü+ ¬Û¼›"ÆËî0¿~ý6+s~Wb]w#$|ˆ”Ÿûþ Ä¥ø˜úýû÷táÂ_9Ü} bc$P ÈÅØj¥a‰T[iq;&àÌxÛ¶mþ7l|–³Oìpžï¯š /^Èyþûï7)©™˜'aL€ Ÿ?¬<%Y~ÿþ~üq¾ûÇýû/ÿžúWÓoÞ½{ç·Øzë–ùõëA»ýÑ£G4bÄݨJ™2¥ÝwðáûûrG&ÀüNÉ”a¶Sîü>*àhÊûäèqy<ÿ#ÀwÿcË#3`O cÆŒòrçs¡vÝûûá`L€ èhQ¯ýÎöþC{R¦¬ôšp`VL3N9MÅ ç¡}Ú[im{õ¶]iìäj‡Å³GRò¤‰Ôk>aI Q«Þôø‰'%H 0—¡Î ‡ ’ž6hЀ֭['ËG%“™ÎŸ?_ê««x’+W.u´„ ÒÑ£GÉÉÉI-³tÂO¬X¢ÃuLÀÿ yq±bÅèÓ§Oþ?Y̰lÙ2š;w®œ©V­ZÔ¦M›˜Õ§ˆ)’L†í¿³ðèŽ&ÀwGåñ˜@$ a"ÊW°x¼3¾%&À˜Q˜0aè—g{A—|Œ„ 0;,]°JöJ?./â¸ß£;÷­&îl”.­“Q_0À"=ZÔÀšZw^$&…cfõêÕÔ¹sgš}úéïÙ“'O¤ÃÝT«Ñì&L »wïRΜ9å˜Q£¢þ¿ÿN6l HÚ`L´Ã5¢Ûµ†¨ü-[¶8t-Úñùœ 0ÐE€î¡ëýÊwk·¤ þ³Æ£iäåået .$¿$N1Œ/˜À;w[·J]B-h6kÖL[ÄçL€ 0&À˜`L€ 0K R¥JR*áØ±c"QñÝûìÚµ«Œtøð¡Z+V,Bß¿þúK~—‡Þ:œã°¤I“J½c­Ã åñâÅ£ºuëúj ׈RO:µ,nܸ1•,Y’:uê$åk´mµç1bÄ %JPýúõe”=÷èçȵhçãs&ÀBíç—éæaè¡ÀwØípŸ:u*õîÝ[÷>þ¬[ Æ+\¸°2.`L€ 0&À˜`L€ „TÉ“'§ýû÷[¼=HÇà0g{÷î5WEãÆ“‡iHÄà0gX¶~øðAjºC×ýýû÷RFu8dVëK”(ùÇZÌ­‘Ë™¹´Ÿ-ìp¹ïsp¸3»îHºÂf™žHŸ>½Œ88p L £ýå·Ü›kµ Q¨çl×¶ás&À˜`L€ üŸ½«“Ûh–ý'13333333SÌŒ1Å;¶cHÌÌÌÌŒ133sÌ;É{SsݬV»·Ç{wÝß·'iH3¥=­TÓSÍ/þG,â¼®(&¤ /öܹsËOH3“`scÂ=ð¯GHî5ܱL AZbĈ’ñs:vDGVËûž?nDbwZ‰3-È“'\Q‘.]:Ë|NdF€`F€þüý_ð$`F€`|„î>‚+ù>&Ü“%KFË–-“úímÛ¶õ‡®ý&oݺe3È¢°ùáÇӥK—èÀ>o„k2Œ#À0Œ#À0Œ#À0Œ#À;˜pv—4ÈÈÇ„{1wœ`F€`F€`F€`F€`‚L¸«Ë¤ìwgù÷ß}|QPA]‚šAº& ûí[œÿþûo‚ÌŽÚGýÒ‚êwÃ/1à¶F€`F€Hôç€LW¯^¥0aÂÈà,Æ £‚ ÚÔ3þl9ÆI“&Q«V­hÚ´i–ù ãÏŸ?o™—&MÊ—/uíÚ•,X`óðóíß¿Ÿ *D={ö¤Q£FÙµDûàÁƒéÝ»wvùhD?>C† ‘dðÈ‘# ŽìãÇ„àž¯^½²,²wï^Ët•8vìXúí·ß¨Gôþý{Ê•+—¥—6H}`wìØ1UÕØbŸ7J2uêÔ1ò­vügœ×¢I“&„I³}úôIê²C›}ĈÔ°aCÂÄ¢Ú»j¸FøžþòË/Ô¯_?GU8`F€`F€`F€`FÀ„î&@ø0ÐðWÂãŽÈv5âÓ§O«]›-¼~oܸa“¦€Ä™ Oigá ••AʤhÑ¢’èUiζk™<ù+V¬H-[¶”“<úXUò¯ï†jß¼]¼x±A¶«´+³uëV»4xŸ(P@JËè^ëÐóVößÿQ½zõ,ÉvÈ›äÍ›WÊ×À{D»n—/_–Þç3fÌГý´iÓR‡$¹ ï~ïØwß}'e`ºté"«eÊ”‰Z·nM+V¬ ¿þúËiSèw¶lÙ(Nœ8Ò›¤½~cAÀxšÍ¿pÆy@ž÷ïßß|J9a‘!C)w¹x ë†É†Ê•+Ó©S§R;Þ5ÈÆ@Vc;{ö,=xðÀ¦ \?äCbF7ÿünèçÑ÷ãÆ«ÊýX±bi²ß=ýGÎn€˜*th?êgºwç†ìE¹*ué·‰‹œöèý»·Ô¾Yeú盬ՠ‘S}E¸_ºpŠz´«'Ï™!s&ÜÛ7­¤IcÛ\‡†Í;QïÁãlÒ¬6®^H¿ô¸oªüúÍ:0á®Àà-#àÆìÞ¾~ùÙcbÄHì÷úÒûw¼ž5S¦NN{´¡ UÊxYž -Üõ™"h¡È½eF€`BúsƒÎ‹…ŒÑó(Ý  šzâÄ )¯b&Û¡¥ ôÙ³gRã鎬W¯^’,…DÊܹs#ÕÐ+q "[½  ¡¿¯fe¤]»v©C¹m×®$étt÷îÝtàÀ€0¨¯ÛÌ™3e=M߇×=Ɔ>˜ nxɃL¶ú€ ‡¶9¤Q”A׎<¸áiOpxÂ:tˆV¯^-IjL +VL5#·)Á¸¬Ì?p†þ<& t‹/­\¹’^¿~-û»nÝ:‰Æi!ݰZ iÓ¦z’—ûЫ_ºt©œ ÀjÈêໃU ØuØ!Í£›7ôs©ýòåËS©R¥ ‚ X”mö{ 'wO™ŠµŒîìßµ‰àõîÌöíÜhí uJ•«á¬8çù[6,s)ö–õËüèŒÜ #ÀUð¬yåÒ5jß¼õï1ÄÆé ¨Ž‰ûí~yvŒ ç0Œ#À0!&ÜCú7À}Æï/î .¤qãÆÙÉÉ4hЀà] lïX„ ÉÊm Hèüù󩨈Q=zt:t¨ü¨4lA¾BâD7ôÁ:͆ ©êŠ<×uMmH’@sÁY­ I’$!ôC·Ø±cK½n=Í«}´?~|»b4Š`´Ð²7ÒœD.$p` ЉGšø(ãW8£­ßÿ Ï®,cÆŒ’ð5ªJ2¶À :þ¸†«&i@˜£¿ðö÷Ê@æÃ#Þê{‚ ©˜Ì€×¼Zíô6mÚ“/õÝ0×wûöíò»‰€¿ø¾åeñ÷nß  ¿  *µšRÌXqÌÃu‹ã2kÒŒ‰¿Ê¾|xÿŽŽÚM‹yJN™;¹cój#)oÁ’%šíÿµ‘É;~ŠÀËçOåµÉ_¸”Ãv?¼GgNzÜãâ F€”©P‚òÊc3–þù—ž¤–ÁÓÞŠlWýœÊæÍ›¥”ŒÒ¹¿xñ¢ô.·"½U=«­wq†œ&A”a‚žç^R?Xà±ÊàåMs¯lÖ¬Y–d»ª{öìÙâ_IôÀã²A¡…ÌH`70q„OP·5ËçÊ “MkQ“VÝÜr8ÐmOœ4¥!+³cˇ„û§Oéà^OùªrUê¸å˜‚k§6­]BÎwön®WžÇÅØ#+ojÒ²}†HéòS;ªT¢6A6iÜt&Ü%ü‡`F€`…î!ëz»óhý…p× /pH‡”)SFOöõ~Íš5¥V:$\5ªºAªÚæ>1Èâá½v‚õÊ@pW¯^Ý ¬1Ù àùîªùgûƪ¬J•*ÍvW r*ðŽ‡ž:ìèÑ£^VÃ5,W®œ—åà_¿~}š8q¢,ûUhr£¯À3¸|7¼Á àš­_é1ѲnÅ|·%Üde”—ûÞëÅ÷mŠ”¦2òýóçO2úïÅKW1‘Ç/ž=rgÅJ’(Mú,.û´lȯ^>Gwo]£hÑcQæly(ŒX½£V¿\}¿÷&ÅO˜”R¦É V!…׋Xî+X._-#À0Œ#ÀôçÝá6ˆtŸ»Œp­öá °Ò' y!ŸáW–={vÕ;íAËÛ¯ Á>Ë qãª,XÐ ÜQ³wwŸà|ã†G@JÕÇܹs«]/·Ð¥O™2%AvóæM/ë ®ÈuÝð@Zpùnèc èýÃûwÐÓ'åi¯ Bä H=w4]Væ¥ ?Ïœ8LÙs´ëª.'S°hYŠ9ŠQ?ÞógŒ£ÙS~#´¡[”¨Ñ©^ÓöÔ¦soݧJçKN EaKÖ¡LÙìÿwºµ©#ƒŒ¢L¿¡ãåy°¿nÅ<ê×­9vådG­-©W‡6$ôb¥Gý¦¨×€ÑrµÏoC{Òª%3 Ò:ÊbÄŒM]ûüJUk7UI6ÛÛ7¯Ò€ž-éÜ©?m4Ö“$KE [t¢zMÚÙ”÷îAtqþñÒþÝ›e¿öíÞD¥ËÛëæß½}]¿÷tÐ “$·«ù¼/„D 䎶 mx}¼(â¹HÉŠÔ½ßHÂ8tӯɱ+oèØ‘½4Bi}pï¶Q “1ìJíº°œؼn)Í›>V€':j'~Â$·Æ?Ú~Uùo^ýEzµ¤}"Þ€ Ü‹¼¤ÉSÓä¹h­¸îÓ' —Å,¶ï?TU¹Å÷“K g§¿^>7òÂ…@¹ò¡#¦Rœx Œtìèߥ–úÐí¢A½ÛÐ1&(˜p—Pð7B Yò$Foð½úø©XÍôWŽƒâÀxy>}ö]¿éñûŒU“Õ*•4úãhçÑãgtðˆç}¿bÙ¢ôæí{:pø„¬=Z*Y,¿£êA2ýñ“çÁz|Aò¢p§F€a0áÂ.¸×ß wŒý?þ ‡Ò‚ ÚèeÑ¢yíèjßž””uÜ´” QR›|ÿ>xúø5©Y” ƒ®[LÀûÖ+´iÍb=[îcl?÷ø‘’¦HCY…çºnÛ6® þÝ[Чž+XT>ðaý;Ò«© }½³úHµ¡¶«7„;ŽÑG+Â]‘¿(SI”?íDËÞìÍë”cFy³ÁË÷¶utîôQš¿r%N–Ò\DïÙ¾žúuonC|#Þç³&¤¯_¿ÈÉ ½ò‚YÐÈAŽå•0Á2jpwzöäõè?J¯JXоie¡Q}ß&wĪŒ)›ÅD‘*üæõ+êÕ±Ú»M%[\CLjÔ­”‡&ÌZK³ä4òô¬\i×´&ÜψUÿþë±4S†TRѪvêÌ%?_@÷›ÏÇ0Œ#|Ð9$ïrQÁ‰; à/„{ïÞ½¥7$[”­\¹’@RC^ÆLT•ñïmŽ9¤–¸:OáÂ…},)ã¯ju>¿ÚƒÚæúéÌ®_¿NÐz× DýÛà¡®ÛŽ;¨[7Ç„“^c»s玑dnËÈÐvÌ“ Z–Ý®¹¬òx.ß »Pˆ½]ÛÖÚœmÓÚÅÔ]ˆ¾!^môヲk²2»Eßáõ­Û‘ƒ; Oè°aÃQÑR•ŒìQCºd;äWÚwDy –oCÓ±Ã{h˜ôV`‰“A?µ¦‹í O£1ØÙºa¹”…·3úýñÃ{šþ›Ïó¼_ʼn[ü/¹>Xýæó*—/NaB‡¢¿¿|•^³q'ýÔõG§Xµ~‡‘_»zYy5|¹S°tCúðÑCòîæÙm”<¯æð%¤\`F ˜ À„{0¹Á`þB¸7jÔˆ† B-Z´^í §ƒÊ ¥˜Iš4©J°­™$óæ M˜0!ÀA¥KîîØ±£ ÷BQóæÍ-à¡1_»vmzõê•q:Ëêß–)S&ÈöÅ‹òT[·n¥9sæP³fÍœžÞæøî膀­^­îÝ»—Š-ê´(ú3wî\£ ˆºÌ™3ËãÀþn ¢;›×-‘^¾z÷!_q`Ï*¦Õz~`ïë²2{Z¡¿®L—“ÜHx!Á;uì á)‘YËvJmtUr¹ §†U H¢¤ì¡E^ª\uU$@¶ƒGM§òUëç‚÷ôöM+ch‡ÿ6ÉÓÃ=® Ÿ_<B¿ôë ËÀc]·Ñ¿ô4<»‡Ž™EUj66²áÁŸ=w!ªZ2ýýù3-™7‰Z´íIQ¢E7Êxg“%^ˆ ïñ›WQõºr9hçú• tóÚ%Ùd"e¼Ô_?°g³qúŽ=‡PFž“•1b§_ÆÌ¦ê¥=yàÈ@Ð/ZØÐ¸¯Q¯…ìgÉZ¬°X#ãVœ>qˆ //«lX½Ð̉;.Í]±×h7aâd‚xÏO±D:&:¬ r;k–Í‘YÉS¦¥eçŽ'´ãQ?|øˆrÂÞîógüNúÅ®)íR;u9å+TÒ.Ÿ€Bàé“g"nÄU›Ó}:í7®Ý¢ÙSçÓÅóWŒ¼æ­mŸ%Œ Þ ’è/Î5€H‘"PÙ’…hÝæÝ² k68'ÜÏ_¼fHРBêq/’$ŽOm[Ô•m`Ÿ`F€`üý¹=Üý[nÍ{ø¨ºé¼ ƒçÏŸO}ûöµÉ¹rå åË—ÌžÆ6…üé oÞ¼”55jîAhdÿø\‡€xd«U­ŒqÞc'ÎÑÃG¶²mF¦ØY­y·'OšrçôpðȘ>•Xq6@~¼ò×Ûã}F€`F€p õ~ƒÒL¸»†—ò~ði³ïß¿—ä3dcŽ9â°™aÆt³úÉÓ äg‘"E$)œ$IijAƒÆ‹?ƒ´ÈÒ¥K ^ñŸ…Çä_ýesŽ3fжm¶2 øÇ‚ÇråÊ•%imSA€\]´hh¡Cÿч:uêÈ~ÄŸ¢FJð†F?ÏŸ?OG%覿}ûVÖÁŸ5kÖPÕªUc«Þºýú믒PÅ„dupŽû÷ïÓ¥K—äXuBç ZxÐ:3`ï UwäÈ‘ ³ùÎ8G—.]ä÷C ÞëíÚµ“7 ÇÓ§OO±bÅ¢«W¯ÒÉ“'¥D·¶ºÐ¡I”(‘ÑexÊoذ>l¤©ÄÈ™3'á{T¼xqxÄ?‚®" ê¤I“ßYe‘"E¢Y³¯X±Bè?%|ÿ°ê¢zõ€õ†ÖûãÝ}x[ƒD;{wn%¯¤f¸wÛ ˆòº¬Ì®-k¨½ì€?²WJ«`?|„ˆT¸˜‡×1ŽoݸŒ´òU“:ÈC°J˜^G&øóŸä‚ð·²x‚t"ôÝaq…—³ÙHSÙÿ‰‰,eðÚV/~b:uü:´ÙFŽÕ8¾z霱A«ç½uý²Ðøß,eiô|µ¯gt9#•mè0a(GžÂ6+Tþu¸X~]3xíC¶æý»·„‰L¨è†Uuz®ÐóxŸp74­M?ë-â…q·®q‚º¬ 8ÿµwQûVž“ÃúW­ÛnÖÚí>µOŸ>ÓQAîßð˜ŠÉK „|@Û­Û÷éöÝôìù_)bJ”0.eÊZ:ót_ø|Œ#À0Œ€Wøá.Nç ¼þÊUG®F¼³U©RÅÕª\.!àcÂ}âĉԧO— Ð‰jUäç¾}K÷á `žeËz>B*ߎìöíÛ„Ù -o|¹cǶ÷üƒ'ó€äGÕ}÷î$‚Ízç*ßj ]r¯wÈ·@ƒ¤6 ^ÌзwÅà‘îÈÛ7Wgê0Ù1nÜ8‡r.þ…sŒ1hýúõ”?~¢+\YÝP¾|yÂ…2÷*Tpˆ Ê/æÅÇ+C _}µÊäwCõßq¬JPüŸþ9Hîk–ÏUÝ·ÛBähÝÆmíòÜ!A—•¹&Êûwo<µw ò]YñÒUl¼ƒo‹`£Ê%I¡ví¶ E;ÊóÓ§vÞÑ*߯·aÃz¬ÜpÖn¡Ko6sÀc•óÚEµ+5Õ¡«î•½yóÊ«"NóÑ—òUêIrÜïð=j,ôÑ¡—Žëƒì äg\±cbeÍÒ9‚x>H¸®ÞCõ¶!ÁbeúC9ÿõ«—´nÅ0‹†~9›ùƒrø€È†¶<ôÚ3ìxðÈ’%‹œTpxÂoþ‰3NñCŽ2)˜D¸|ÙÓëRïT*V¬H=zôzöñP‡xëÖ­³ÓЇ–=<-¦M›æôÆ)›Ñ£G¼+H©€ún lÛ½{w›ÿ!¬: d;ú@x:#8ª3;æ!ð"HFw4Y¡ãž9[zùü©ì*ˆÙü…JÙt;yÊt„1Áîß½I %•ûæ?¾ya#úéaÃ…3qzüßÿyJºè]ñÖËûÅ~Òä©fò J•¯aC@#®Ø ŸZd;‚¡9bǵˆøŸ"ßÈ5Áîܺj©áŽ<}ÅŽa¸%NšÒ,‹€§‰“:^áQËúïÿþç3¢Þº5NeFÀ÷xõÌîû38nA—•¾aË^j\¿ŠM]NFKµ)àÂA¯ŸGK²=A¼ØT¸@NŠ.HýBùrІe“eíZ»Ðg46cŠ;¦ðÔ %}ûÚô:Ù^½RIjP§åÍ•EÄùHË×l¥AÃ'Éþ›8ú÷l#äC]÷4ömÿ|ZÿãÇO´|õVÊ.%åʑɧ͸M=¬$†c—Ùà>}útù>ƒ÷AÖjU¹^å°šXwª@>“tÛ¾};ác6H®Âƒ^ º*ßJ•*%ã‹™ëàÄ>Þç`xGÄ;2V…› ^ñxws$Šó ÕVß› wé*ßÜ>ŽÁO€¨ß½{·ì¯ùý Ò¸xÏVb³¡/p„C;ˆÉ¥ãa.ËÇŒ#àÿ褸_<3 =Lâ^Z´hQ/0wî\Yõüâü^ž ¸->&Ü1›ílFZqš4i¤FµžæÕ>f¬ññOƒ<þð -qÌðãƒ_¸ø@Cx;’[ðªxØÙ¸q£üÁÆì:– aVÐì†Ö{öìÙ}ô㌶ññ©ÎÐP†;>Ðk‡¦:0ÀrE`ŒIxì;óÄÇøðЃ#! x!à! ˜|H—.ü@^ÇU ¨ï¼ °4sõêÕòû™"Ä*æLNF¼Ü»ôv|íô²½¯ËÊœ9yDûôx™E?J”­f'§XîðºÎ[°„e—õ@Ÿ é]±hÑcJ¹”…v¹•ähKšÜs²c‡Ç‘•ÇôÍk— ŽÉ ß¼ØÿÙ_63¬GCƒ¾\庖}0ŸO'Ó«ÖjJe*ÚË«¼ym#Ä܆OŽÑ釪CFϤ˜±ì'û;UÚLá¾jélËÉL@èç0N&v’‰k†ë;'&îW/žq^m•k6&ܯÙF€`¬°’•Ñ ÷»÷ÚH°Ô­YÞº!/R¯\¿M ëgMJ"„·+­¿“/œ‡’'ó»ÕH[v0Η$Q|Z4ë7›¸ýÁ~êÌ%Z½a§XMõúó•/SĨãn;ç.\¥és–ÓÂe"®ÐÛ÷´tÎè`A¸C~ïOx÷\Š2¼[ ]½vêÔ‰ö /÷—/_Ú¬† sʸĪUeð~‡³ˆv½m¬GL08Ñ)Ùïú» ˆçýû÷«¦Ä÷7eË–MÊ·ÀK$»~.XfžèpJÂ{£2p( ÎlnxŸ2<ÒáŽÿ™dÉ’x ¼sBŸýÄûâ±cǤs¿tCL2x­£ßºáñÈðŽ ‚]é4ã÷÷ß—}zyÞg€EÀ¯ wL"â~8Wp‡^îX„ Q˜ª°£ç³¹>&ÜÝi¾í fâAÿ4<$˜(üó|îØ6&ÌÁdýªŸ Špôê&èóÄw27ðjjöòÅ3ôÑñª }<V- N½†ºDêõb_—•Á ´.YR®r».Ô¨Û\jr#cãšET£^ Êš#ŸM¹W/Ò²SŒ´õ[ûÎvâ'Lj =*…–(k˜ùÈt÷¶ý …³6ý"/eš œ®\<#Ï¿fÙ9n½mxÞ·oV™Ü»-“§/Úªgûx¿BÕúáþçÁ]F;®ÊÉ‚úˆ¸ÿŒ# ¸*'£:Y¿"aU›~µ-[©Íœd¬t…šÆC¼ù< ŸŸ=~HÇÿÜ'´(?јa?™‹Èã ™sÐàQÓ-ó¬ë4lCó§“²)ÿˆ¥½ ÝñQ–&}©¿gûz•`Û\ùŠP‡îƒ¤:fO%?怞<£ü¡4çùôÞì'0ªWü6áa$8Ù)Q¦*ÍL1Bê¿c‚dܯ}åG¯‚@°ïß¿¥¿…®è§O©l”ôÛ¤ÅdµÂA¯çÕ~˽ÅäDY Ò/ ÌÍ/AbCç}ÁÌßéàž-´a¯‡ÌØ)Ëè§NhïŽ ²{}âªCÁ„‰élõVÕ9Žœ*' O·ÖµU–Í«O\²©ÈŒ#À~ñòì›n[ÉÊT:çŸ<§ÃGOMûT¿ ¤K“Üh'0v^¾|M󯥕ë¶Ñ…K×éÝûÑ —Î oö%+7KÙ˜ã§<õ¶Uþ*<ö‚›ð!R[ËðZW†f«R« áã̬Þï,›’&Oí´o¨œ=WZ³ã¬œ<¹zùA‚ÚóÉS¦%Ä>±¶]>Ž ò,§o~r”-Ó4>V–0q2š4w½ H OïOŸ?Ê ¢ ¤AºÃ@^—*_ƒ®ŠëGL¨€¯ÛܲjÒ&mìÔe6ÇúA½¦í©zÝtM`õðÁ1A‡’ ¬ôª[^§3Âýʘ9§^]>ØÕjЊ𱲧ÉÉS¦1öÕ&Bú ùz'åаáÂKò>Qk¯W¾Kª}Þ2¾AàÇvMGváŽcm^Gu8”¬ÌßBüÕë·RneպƩ}ãÝŽF¾ûîF[¾Ýùï?5 `ÛÒ{áne­: ¤»‚t‡U(S˜¦-BS^7%Ÿ¡§Äþ{¡¿xÅ&éÍ~Rî>±¥ÂþüÅkOwïÐt‡7¶í±È“ù_=ò¿•5ÊËÃñç¿¿Èn€`ñ­Áû„‡N¹Ú&Èx{?~üXêªCF¦jÕª2è§j£k×®RÏ\û×òÀ gΜ‘7ðxñC?+V¬(½7uo|È!(AïSƒ'?&”ABý‚—:H*hÉC®@לWeyË0Aýª&};"xŸÃ$9VÎ F„nÐ;_¸p¡Lrä¯Ê9r„æÏŸ/µÖA0CzE7Äá@l¬ðA›#FŒ·zücu#Dz¿~ý¤t ÊÖ©S‡~þùg½š$¾‘²ýüùó6y¸o‚ˆ‡| &GÑž2œ“ˆqØ®];CdË–-ƹT9xç£,b]T«VM%Ëm«V­R3˜p~þü¹<  =ûø@Pïà`L¸»éUÄ̾¨V„»¹Ë(gc.ÃÇŒ€_!Ù-ë=–úx·Íu+ç»-áîݱèåAž"¨¥_¼ÄýÚSÜ/ûrä{P2èÁããÈR¤JGøøµôΔ-·üXµ /ü‚ÅÊÚdm^·TèÍ’i¹ò‘„¹Mq€ÿEÈ)KžÒqßñà‰É|ØF€Jè/ÎîÒo³¬ÌŒ¹+%éŽþ*¯]ÝöžÐýŽ#ݹçAš?yöÜòô7oß³KÇ‹¶.‹3kÒ/'¶ýʺ¿^yH~Ø5àO 'O_”$;ÈvG®žúýÁÇ Þ‡ðnôI`:3 K@ˆÀàý²Ä Þ’:xJ— þðßM ²áµoFåѨNoóž={Ê|¤3†tÂ=gΜRŽïÀ ÃAŽGŽYU·Ü‚PƒWe˜0ad>&PtÏMHÊ€„RzïªôÞ¥lŒ#¼ПüŠpB¸¿)Âò2úy@cuä[°ºÆ™áþ„~UªTÉŽlWõ@Vc¥HqxÎ뫘Pˆ:Ù®êAjK™~oUiØBn¦OjbB޼0L&èd»Lüö¤9âs`õÖ´iÓìw¡o&ÛU:&~q_G óoÒ`uëÖu*}& ‘?ß‘~†¸nB m„@œ`þ ò1¾ÌX’ôBÜE¢>rp§˜ úž ›YxGŽ•"EŽBa†³¼hÉOX‘Â1´x>昡WmS˜FÀ¬9òÑÕKgågɼIb’ËãDzóúõïÑ‚ž<º/“/ ]¦ì*›·Œ#À[üòåÙ7 é²2Ë×l¥ÙË—'+%Nß7M{Y÷ûï=_áÞ¾³—!IšØs¥Ýî}GíÚÛ¹ç0]»q×.ýÍ›w†^ûwb¢V'Tax‡#8i@Ø»©P醔³H-š>w…¯Éö€è³oÎQ¥J‘íêœX­šBGËù•uêÔÉKâZ•õéÞùdzèÐ!ILaŶ٠ù«K(wUÞ“ÊÛyðô„7¼#ƒ;H)hÛâHÉó€üBAlÇ=R>ÁŃÒ6œÎ„Tôß.¿|fÀ=1îܹ#å_t|U°TÅjòOÏ×÷±â'[ÌŽ>*ÎäbÌnÐÊôŽ&1Ak6èÊJ–,©ví¶¸—+VL¦ëuô‚ð€·²"Pƒ d–YVT7lØ ó ‡\Œ=ÜÝøJâšÜø…Ю-Y‘ž{f7ú•‹g"‘¹jÛ))[aWFÀKà‰ß°y'Z8{¼\éÔ³}}5¤;ÅO„Þ½}M÷ïÝ"…a2kø¸¹rY¢— sF€`?A R¹b&t(‚¬Œn¾•“ÑÛr´/n,zûîƒÌî?ôjR¿ª$«W.%Ó äÍ.žn—ûÓf/—Ò)ÍVr"á„'þqê3h¬eÓÑ¢E¡dIÐmAtÿ'HÉêõ;Ñþ)cúTb™÷;Ú¼}? 5ÅЪG#'N_Ò3EÄpÏ¥ç–û 1©èË–ÕÓhݦÝRJfû®Côð|öý È‚ðáà ¢ã õC(±J8Ô·}q,Ò<=Ò=‘o_~ñŠÍô׫×2@¼±êåÔÇÕcxq+¯CŸŽdu·nݨO¸4Ѝ†§b—.]ìšEÐ>9…g¦2HÓ ¬™0‡8t‹áÕ©èA¯~ìØ±tíÚ5#kÇŽOKÄ#K™2¥œÀù@ÊC§X™ÒVÇØ"°æ€¡Ž`€åÊ•#H:@êAAxXWúô¨™õÀ¤˜ÒÔ‡TOÑ¢E ö ºHÐ<€Õè/™ÆF `ð/„1‚\Ÿ+¼Îñ¿{ñâmܸQîà Þ+CàRVáã•aå“Ù rᕹRFµ¡îßøÍ½֙)/zè»CJÇT÷|Gù)S¦H-yÔ‡Ü d;tßáµX ÁŘp.W’ÇÁ2æÙcáÈ=âÓ3Aè®ÇŽŸæLM¯þz!ôòË>ª¢¥*Q×ÞÃ)Eêôz2ï3Œ#Àø3‘#G¤²% ѺÍK°q:x…תæÿr2¥Šå§«×ïÈnÚ¶Ÿð‰= )½M‹:4vâ\ºÿð }DàÔÙËäGV²fJKI„¾Þw•×§{+‚Ž; Ò+%*5WYÆu•Îû¸IóiËötù¤}L£‚/v"Fqu*ÉÏ‹—¯hùê­´xùF)}ó>h·G§fôëàn>¨i]e÷þc’p‡×¶"_¬KLjûöí $8HieH‹-š:4¶ 8V­Zec$6<ÂÍÁUAªCš¥sçÎ6å!¡0nÜ8Â9Ì$ d^ eSéÛˆõfƒ¬ ¼áx™=ž¹¬:F[Ês“ÔkÀhúcÆ*Ú°÷Mœ½–ÉvßA̵FÀÍpöbØ]×eeЗÂr¼ÏýÛ÷í@  6LhãTº£CذahϦ¹T¢H^#;˜@Ú® ³ :ïVÖ²i-š8º?ÅŽé¬]/“%cÙî郫(–ƒúzy¿ÞGŸÛµ¬Gw,¢ÛvЯƒºR&á}ïC0Ôàlfovåõn5fh/^ÜFþÒ,ÐX7ÿßÁ;ßÐ‚× Ç®á ¯Dx¢C6¦D‰v^ªd† &½Ý•ŽÊS[|=|ø° üjî‹*ƒ-$†*ñ¡ïÊ@Ú °!dZ­ ^ïfMdLJ¨±ÀKr6zW´rL÷þÇŠfÊÐWJ®x¹ª:¼e¿E@¿gØq¾Xõãß–&My L”šW™Ï­¼áq?4{·ÃC^Çß\Çðr‡)Y¬ ÂJ+õ 6”yÁå{¸—+Éã`Ü ¯n´nÖ]î#à¶àáAUÍUݶÃÜ1F€`ü¿~yöMW•絫mT([„þï­ÇRrsÛvš“G•Ì)?Ï_ü%%cbư%BS$OL;±þ@x¹_¿yWHм§Bùrˆ€nåfNJøXYûVõ©EãtîÂU|õ¡ œš6u2›ª7În“^æ?}¦œÙ2ÈfœÏê<¾IK"tê{wk)?.]—^ïKVn2‚Å:jûë×àM¸cÜðT^ƒŽp@:¼&wíÚå°¤U”¼Š^¨wïÞ„Ùൠe9vLÄoì T%J$‰*MºÎ°*oÞ"à*´Ö>|(½ÏäÒ ìãÅ‹'ÉtÝŽd7n,²=zTÊÝ€HAó+I„‘#GšO+ñ¯U¯¬|ùò„#À¸:á×Ï h»I“&RZj®•É–-›ŒûŸ«d1b1þùçŸFÜ GÈ­^½Z’ß™3g–“˜ŽÊùEº"ÜÑ~yÞcõ¤´`z™àâèÜCþ Ä=<Ý/_¾,W2U¯^݈CâbSn_Œ w·¿DÜÁ €ÀçÏŸéÆò¦r,$šùÇLÿ¡ ‰xð˜F€`F€ðobYx¢ëçL˜ .áã]ƒ—|åǪ®”Ô)UÈ*+ÀÓ 1?\x»Ø…Ž=#ôÞ7Jé™çB‚Æl!p79°ŽcĈ!=Þ}{~x®ëÞëÞiþð§‚üy§.—e ‰€ÎC˜9 ¿wHK!˜©ŠkC®J¿`ÎìÙ³eŒÎV24çÏŸ§:uêHt%·â}wÔVõà>¹gÏ9&¬ðA, ³Íš5KäHG ŸÚ­_¿>M›6Mz¹+y%æ“6ݵKʸë•á~KáaQ @©ßd:ï‡õ3?ì7Å0Œ#À0Œ#ŒÉ’?o6š8ægzt}mY5Õ­L‘"†7FýÅäÖÈàF€``€îX)rüǺuë$f®ÊÉ 08£Ê•+Ëú ê!)£¸l\d6bKÄŽ[– ˆ ƒMX1„UIh­Ç¾@< Ä¸èØ±£ìJ©R¥|µºGÉÊ,\¸dò_eÊ” ˆaè9ØÃ=@áæ“G&¨S§N-?~ÝpG3fÌÍ"ˆlp5&܃ë•åq1~€€ZÒ´gÇfÊ&ŠÓ¿~ýb“_©HztÃ&‘F€pC¾~ù"ôÃR‹úíݰwÜ%F h ðõëWé¨þâì/'àFF€`F Ø  ?7Ø9›Qí,Z´HêÕlj‡êÔ©´ã¤÷L¸;‡³ŽÀÛ·o%Ÿ?ô6>©ãí“pF€`üÕcc÷GÀ2Æý;Ì=dF€`C€ ÷ƒÚ['Ú¼y³Ô‡üø1-X°@ÖíÑ£… Ö[í¥ÂL¸¥«Å}e¥¥U p)ê5p¬Ó³¯_µfMi”™½lňÛ8æF€`Üe²Ó?ÿ|¥Ñ‡Q–lݵ›Ü/FÀ­øeÀo´o×A·î#wŽ`F€`‚7L¸»çõ½yó& 8Ðè\Ù²e©[·nÆqpÜaÂ=8^U#àÇDFðŽÔé¶#–-¹ž4yjŠ7¾Ó:œÉ0Œ€; ð¿ï0Aˆ‘"¹>s‡F€`F x!À„»{^Ï¢E‹R÷îÝePל9sJ)™àÈœ w÷ü.r¯ ‡€i·þCôÃ=fF€`w@€c°»ÃUà>0Œ#À0A‡` w÷¹f™2e¢Ñ£G»O‡ 'ßÀ9øŒ#0ÿ˜é?t!`ø#À0Œ#Àt‚Ÿ‚ÈE ¦ÝdÂ=˜^X#ÐØý˜±KZ@_>#À0Œ#À0Œ#À0Œ#b`Â=Ä^z·8KʸÝ%á1A3á®ÿÐÍq¯F€`F 8#péÊ ºpéºÍóçÉF ĵI³:xõê íØsØ&+UŠ$”-‹ó˜76ø€Æ<þœfÍšeŒ°V­Z”"E ãØw®^½J»wï¦7oÞÝìÒ¥ … Ö8j;xW;zô(>|˜¾|ù"»=ztjÕªUP ÷—pŠ€ÎC˜9 §9“ðc˜p÷c@¹9F ¤"`þ1ÓèB*&}<ÿ—2fÌèm¤ýªU«¨lÙ²”4iÒóÁƒ„¾þóÏ?6çkݺu&Üׯ_OU«VµSªT©˜p·A„‚úsƒ™£ãã1XR&è\+î)#¤Ðè‚Tǹ³Œ#À0Œ#à¶ø÷Ëó²U[èßÿõrüKWmö² `ß!н{wjÛ¶-Õ«WÏw y£ö÷ßOÁñ=ãbcBúÿ¯?3„G€=Ü}Ž×d ó™þC§ã]F€`F€`Ü'Ï^Ðî}G©Tñüûxïþ#:rôŒÃ|Î`¢hÑ¢Q³fÍ (%Jd컺ùØñãÇéëׯ*T(W«ú¸\¼xñèÊ•+RR¦oß¾/ûà`+V¤“'OÒ®]»¨W¯^ÁaH<FÀ‡0s–1ñöíÛòþæ¨ ß}÷… ŽbÆŒI©S§–÷UGe9Ýý`ÂÝý® ÷ˆ¢ü_í7w›`F€`O¯Øè”p‡ˆí>ªQ¥´]ë×Ù ÂÂ;yÒ„^îèó¯c¦Ó“гÏ ¨1Bx*R0'Mûc°9=oÑZjÚ¶/ŠQ÷ŽM©UÓÚT¿E›s…òZ×§1Ã’/â=ú¢™óVÊþËŠâOœX1hÄànÔ´¡õoÆÓ¦Ë`:pø$}Õ‚D~/ž)råÈHSDY2¥UÍÛdKÒ{äñ——giËöÔ¶ë`zôÄC~#³˜ÀÀä¬A튴pæ(¹oþ3vÂ\ê.ú +$îû·-0ác îÝ»G:u¢#GŽH©I ô”ìܹ³M[·nQ—.]èèÑ£FYxŸÃ«mÀöíÛGƒ ’„ºþ?vâÄ Š1¢Ì›7o}üøQ–5* :”:tè ÕŸÇSûöí R0 ¦uCàÓÅ‹ëI–ûkÖ¬¡Q£FÑÕ«W%Ѥ PÖ‰4¤GˆŠ-J3gÎÏ1öd˜$˜0a;V䪭~øÒ¦MKÕ«W§Þ½{KÉ•çh ’|úôéÒ»T°@0Rœ2:VöðáC?~<íØ±ƒÎŸ?o÷Û ’¸õìÙ“¢G·~Ríþ÷ß4yòdy¾;wîÈÿ}•Ò°Ûž={hĈ’œSùζÐË_·nýõ—ç= >®qóæÍUå=Ø¿kÒÃËêáÂG t³Q‘å©Y›žnE¾û6^‚âÃ:æ"FòsÂ(ðV€¥w(x4ùph6ÕöïÞL}»4¥×¯^éu·¥þÃ&Ǽ´èÑ¡/½Ä¡w $„N¸?zÖO=•MDULˆ&°iîè¡ãÔ£C?™V´DAA¸O³Éé^áÒñq·ñ7¬SYîè×¢e, ÷¥BNFYÃ:•èðÑÓêÐrûêÕA”÷¤­;Úå¿ÿðQž/wÑÚ´né$Ê™=£]$”48'ä{ô1ÙæªT©B €u!Ó£Géx+7iÒ„2dÈ`#Yj”U«#áíž-[6¤«n åu²=sæÌ”.]:£¼¾u«\¹2•(QBzŒ£mÕ¾^Æ«}xÝãf¹%Õž¾92ᜅ ²iöÅ‹2r+Êà©_ªT)Iˆë²+ZX«V-9 ¡ÊZmÕ»$òåËGõêÕ“šè 3x÷ÃcþúõëvÕ!Á£ ãJ™2%U¨PAN€WcÅD•-UˆÚuBSg/“Õ:tÿ…Nì_aƒz[W®ß¦:ÕËѬIC…ćDŽòë‹þNž¹T]¼b“˜ (¯W£åk}zÚ¾}»áe3gN:~ü¸ôä©>kÖ,Yö÷ß·ñZ†W¶òÌ.P €”„‘¿ýI“& M›6Š)B4‡=Úi¯¤jvîÜi$¡ §½c 4 |`˜D€V: zåf}c™añ§~ýúF=ÉðòÇø9Ž•†\©Q£½}ûVê#wëÖMzZ4g$aüðfÑ­ìÌ™3Ô¸qcy ¹ƒ ŽØü¯ 4HìÀÁµ3뵃dWò>ã±²áÇÓÉ“'edlþøãy^UïbáÁù­V¨ræ-VT«VT€ZäÃ#ß…—¹3þÜ`æ(ܹ߮ô 14`æ9¸ïbB†ûP¸páä¾ùî=_ÅDúÊ•+åýOåÃk¾oß¾ru &÷tÃ}bæ‰M½ ï[#À„»5.œÊ0¾D@ÿ¡37•#wAªßÌVóQ/3°WkZµd¦Lº|á4ý-~XÂo’n¡B…vˆÛ›×bqÏi×Öµ¦'îÓŒI#¨Ÿðz lkÑî§ÀîŸß„À]1¹¥¬@‘2T¨˜×^šªnÃÝ*ú÷ËsƒÚ•$áŽqƒ$Ö ÷¥+7p@~Æ+?u!Á»6ôçNÙ®×6  ͘»B–›4c1õéÞRhfGЋPl U'Û‘™DÈ;%¤úý‡Odٺ³Ül¹²g2’|“†B<Ëwî="ó Õ>zXO;b ÏqÃù¿`ézú ä`Μ¿B'O_¤ÜB·ÞlðlŸ3e˜xÁ·}VkÖ°ºA¸Cvò:Ñ„ü2µZÇëUQÉN·Ìj>Ó Á<Dˆ"ÜAò‚pŸ={¶1jh°#Ð)ˆyè´+óqOnh—ÃC/Ë:uêÈj϶ðì‡V: ä$nÌr3ð(/Y²$­_¿ž - DzŸ>}d T«ñ¤J•J,4‹…÷:äd2fÌ(W@îd>Ú×-W®\Rמ©†g=í¸qãR̘1¢Ðç·2]ò'ð¦× ÿ³Ðn‡D¾˜TÐW#èeÕ>¼W1Á‚@®0à5iÒ$©ï¯Êð–jè<„?3$6´Œ{ LMúÅù±R©hÑ¢„U:À:ò±cÇ–‘ø}ÁobC@ò*wîÜ~qÊÓKÊ„˜KÍeüo/”ê,úJsu[¦bM£(¼hž=õÙR¦¯BÇzÙÛ6®  "0$ˆ{ïØgñðyîôQÚ²~>q˜þzéÌ;mTÙ(Q£Ñ3VQ“VÝŒS®\<ƒž=qŽÝ£w m¡?¯tÅaçñÃ{´{Û:Ú'´ëoݸbTÊY—s,_þþ›ŽÚM{vlýÆ÷Ö¯ ßC|ÿöl_ï£ï ê;²—Ö¯Z@O?”Ýzÿî­Ñ½H‘<É#QÛA`Uüàºl^»„ŽÙçí~ÕëªÁÀ»ÞDà³ Åíû“vnÝC7®Ý’‘®4z'¦[vÓK-¸¤+uQõm%Y¯¿ âÇ»dÙjÔºK§õÍý6z¶¤5ËæÈ¬ÈQ¢Ò´…[)SV™syß·êЇ–ÍŸ"&ÎOÝúްiý™1ñWZ8k¼ qŠÀ«¹ò¡#¦Rœx Œ:Ãt¦Ås&Êãl9óÓ‚5Œ<}g̰ŸhÎÔÑ2©rF4ü÷¹rzå6eKx.‰G¸g+µil;n”-WÐã…7JÔètè¼í„Îí›Wå÷æÜ©?mH½$ÉRQè^“vêtƶt¾ä„‰Øé[Ÿéàž-4¤o;zþô±LKž2˜¸,÷ÕŸ «>°YKwPžÅåþ‹çO…^ðÚºa™]Àcx†»÷Iè•¥ëjÕÿžVµt]:sÒC¦@a=r|`ƒE è&-¨,c‹ ­ÝÛ÷ÿûåÿ¶ÊÈ9 ÕŸri+»yý6ýÔy€˜\:kó}O–" 5k݈·¨gW­`ÖRòvýÉÚ»óõÿ“Ͼ¤“g•`õrï£?E ×A}~¥ûwm†ŠZ´mB{µ³ôBƽfÒ¸4gÚ›‰€ðB>#OþœôëØA7~£=Ÿâg4àË+—®QóºíÄDóßâÞÊ—­…¬ê¸¿Á›ÁHá¯ö®šÐ‰Sèæíû ÈÎ@~Æ™á;£Ô*ꊽ²˜üñê\h×Êëûûï¾·<¥Þ¯ÉY–Q‰)’%V»b<žD½‘(vM LóF5ÄsÈHY|‰X1ðc§ èÒ+kP§¢ü†J7o1á1GzŪ€0aB›³Cä±N¸ÃäŒ. PT$­2³J·ÚB=¨¡*CÑ'O>~Ȇ|DÛ˜PÖ¿# íÛÞî;Ž|eˆ‡Ð¥UMƒlWé®l_<{BÍk—ÒNÞ¿³«‚—Gx¼7©Y”îݾa—”¯«Ý`8Áe^ ¢°^Õf´mÓ.² \ ôÜ<<™òG™(Q¢E¡s¯“í)R¤é#FŒšÉðÀ÷JâGoïÝ;ûç2ãd>؉ßc¥ îWÐnïÝ»·×¿šä*Œ@ " ÿVyõ¨õÁÉÇ/W¸èÁ¬}ÐŒM¬º!ân˜­aÆôúõkù£<›Ëð±5O?ÖyœÊ0Œ€Ëx¾ŽyTÑèÌÜ¿w›Nµ÷’¾vùmäå©ce•ü…KQ÷o^Qæ6¬Žo^¿,ÈíÖ)ƒúu·%x߸v‘V/™- ó„GõoC{R¦ly„®­çfx¶Ã›[¼´4ï(ôocˆ@¤ÛiÕÒÙtáÌq)MÓ®I%Ú~äEˆI·Ü‚UzôðÖŸ¹d‡N™Î²¬_%&O™ÖhêÁ½[Æ>vÆ ëE‡ön“i)ÓdÞœ¿PZ4ß— ÏøMkK‚¶™ [1>LÀ?iòÔtç›î÷ŽÍ«$.zÃ×.Ÿ–ǹbÄŠCy –гîíÓNbŠðfïÒg8åÌS˜ˆïÈn!¡²tþd驉ŽY“Gþ?{ççDñÅñ'XÞ{•*‚ˆAÞ{Gšt)‚4,ˆŠ"½WiRE@zé½÷.½ úÿïoŽÝÛä’\r—Ë%Ùßû|övfvvv滹ìæÍ›÷¤•fÁ¯KDÆ¢ŸëýÜc•«†6>“r•jk »'²xÞtu¿±ÂŠêÑC¿–þß–œyòËðI Õe»¶­k(èû7F’$K!ðϯË9-hp >§X•ña»On–âÅO(;·®Óîa5Y2{ÚhíóO³¨Ÿj³¬ÕÃ'Îͯ1…}öœy´ë¥Tn„09y§p iÒº‹Jg{¾òãçú)×8(Äç¿eûòŽè8yÊ4râèÕLJA1?GãÐí³Ðø8'ï+úŒr`ß!Y8oi¸C+Qª¨öY‰/}¾ì.wïÜS®Sæþ²@W¸è;†ø×s„]Ù‹x|f?îÞNà3þŸ'ÿÈü9Úÿñ´yÊÕ ¬ßþqŒ|3¸ŸÑ³§ÏIç6=”‚ç¶ý¸…²JŸ žæ¦i‡ ìÿ£RrOŸ4[â¾Wzö ]²o4¢%PŸ÷äš/é‚…ßÖ&ãKJJÀ.m{J6­ï=´v²fˬ}n‘SæÊò £§ª1š­ÕôûAÖ­y.½ž=‹tÿ¬“Àb“S'Ì’…¿.QŠþúÕšËÆÝ+$Qâ„âgODÓׯÝF5[jßã7Œ&^2K`_üxÎ÷fNÉ©}>iÝ;5¿åG–9ó—+¦©R$•’Å |%ðyÎ’)½duîÔ!’9S:gÕ}Zn¶H?yê¼ËkŸ`µßøñ!q©p\2ôéÓ'ÌŠÜ7W°ž‡¢ Ï9¬dÀÄ€>¡â¬?à ÿW¬ÕÏÉ”)“r!…Ú¼yóTñwß}§ÎÂŽ}pWý<îIÀŸ ˜ÿÝýÞð‡ñ ¦„½…9ú…É>L¨"Ð)V,!.ÅáÇÝ $íj|XÕƒ8˜Ìà ¸£ªX±¢ ÌŠï²—4à 󤟫¶xÌ–î¶<˜#ˆ û‡Ù âüÇ×ÌÉ#›+É£)Ã?_["ÛU5›c5e¹nY¦b-4âcÉSÊÔé¤h‰òÊ*xÎô1êPîEYá@áþçò…ŽW¬Zß`м3Ù¹u½òÕŽãpg3}á&A? àU pq¥Ðž:nˆ*[¶p–¡pÌXTc^øƒÏ|¯/†Ú²ÍýæÛ;N\å²—À$$Qâ¤RBsÁ1/ñ‡û{—I?|Ý])ÛQ÷«'HµÚMT’:myëbR½ôjâg攚Œîjeƒ^GßÃ%Mù*uåËÆk÷5Ž^¬ö§O1òP¢ë}Ó 7¬ ]öܱû—R¯q[ý$NRR¾þq¢Ô,ò£“f ôûjK0¥Ó¬t±…'KÖüªî åWUáêE(¡K•+¡gîûiߣfW3yòå–8šÛ¸c>úÙC~Àçƒ køA?-µêWC±’´ZPÈw ç—2ïVS®Q¦N˜©)ä?TA@õ:úý¬\½¼|?ì+íÐùs#eª²à_´ïœw õ4wå«”–ÂyJ+kuü_=rÜps@³ÌŸ;#äû-‹¦l\ôçÃåL*M¡Ÿÿ|j|¿hJ{X»O5ESÈwÖÜsEŒŸ>žˆìïiþ¿”Ñæ@IDAT›Öi#çÎØ*P_ÒÜåP<'ËìÞÚw<¤C·¯m‚“B™îŽdËšÁP¸oݱשÂ}ÆìÅòø¹[2ÅÛ¨³Â}Ëö=rúÌɘ!M˜KÞ¼y[–¯ ™lÂAóya*»(@ðT(Ü!X1pçùê äÝ –:ùùÿ!ÎùCë|ë§Ô&?("P¸CYb–F ”¨p+W!º@i‚ª"fŸéýõ—R™ûŽ`§‹/VÊb󃻸@@ðÐ#GŽÈÕ«W¥J•*²víZÐ|®ž¾xñ¢R\!À€ÎFZ·n]›*XAûÓO?É”)ST9”Ô%J”PéãÇËýû÷U>Ò{÷îmóŽŒgõð¹îJÀÊ~0€‹²…"¾ã ,êqïQwÕªUN' ðû »9sæüÉÃz2cÆ åÓyþüùT´9Ì2¿&¨ ÷téÒI–,Y²E ÓÊ•+« SÄYÀ÷Û˜1cÂÓà°1»B1”ízö­;öR~óÁ<]†,F"3sû‘IÇÓÕÖÓVPØË;EJEχ*+B |Žáª’5{ne»~ôu¶VYLŒà>9X¶C1n¯lwT×\k¬¯~˜ Åø]mµ¶2VéB—3ßµû?ôûf°,p›,ã?ø°~˜ú…‹4ÊΟ½šÖ‚–®xîVçfe»^)]†´Ò°i•…B{êø™ú!›=,Û>À¥²'´h×ÄP¶ë  ß9r…¸d@™¹“ÇN7¾7?éÕÑP¶ëçb‹wý{ý{pÿù°OÒ5_í-µ—Cl'4pqZ(Fì4Ôü¸ë¦«´•º¸ãNF¯Û¸AU=)_?ZS¼…ýlŒÒ>3´ê!-;|.§Íre;:”^›Ì*]¢°êÛ¿š²°[Ÿï ƒ½Ãx¯ëÙo°<ÔC`‰ž?_.ý°Gû e‹ £Bvh¾ðGŸ¥Ò/jʾ†uÃÆ7QM Ðœ6k‘Q‚>O›ò¬4 -œpä"Š(i¡x7 ¬í'Œà*A;Ó¦M+I“&•íÛ·§,XP•¡ršƒƒµüŒWªTI”U¯oV`ÿñÇF9ŽçÊ•KYá›Û°OçÏ2y‰rŒ×8p |òÉ'*p ,Ø¡Ÿ={¶*ƒ(ü ×½þÝKP(²hîRp>ÚA>Mš4jƒRÛ¤I“Ôw~‡”âëäÉи0ê©W¯ž@±ã_ýµZA¿™É—_~i¥ET=àìÍ›7J3¸méׯŸ œš1cFÕ;wCß»w¯âŒ1šeôèÑꞢ uœ[£F ¥(‡U:ÆKU0Ã}@ðC¸@ðCȵk×+p·|&0i`öÙ…øàžCO!@! ¿“¡¿ö:Š@ƒ³~~øá‡‚É;”äÞLâû+hð]R¨P!õ ëú%K–¨ïLô™ãÛyëÚÁÜ΋Á<8ŽHÀ‡´ff1?èÌåH7iÕE*;ðñ}ÿþ]Ù°úw˸AÍ.mê¨ °xw%æ@y5KíTiÒ;¬_S’ÂÒ.J 8ï|ïˆÙâ7Ëë¹4×!~/í‹l®dÔЯ 7)¨W¢te±íêo»ù÷u£9XŽcÌãGöå¸?»4ÿàŽÏp1E=ÀPü‚gþ‚Å”+ ¼¸€Ÿ´÷ “Œ™³i®B9j_/;e²²~óypQý˜¾‡;“¡ã~Õ³Æ>2c1‰dŸG~ì’j.[tÁ„‡'r\s×¢KÊTéœÞ#ÞÕåè¡+z=¯ïëhŠòX¯†XñêeîìaYùnñ²6U?z¤YÍž3§Éу{•6•žgý¾:S0”•¯RFŠsý=Šq¦J"ÂÃ}={f‡ÿÉLV¨æÿ‰c‡C\J¨ë¦I©|¸;ºxüø¡ßLJuTE6©£}ÞÃN"ÚWΑëuû"•7÷ñ?Óÿ-‚¢}oÂϼ#åü¥‹—µå¸÷U WL øJÀ´cËOdÛæP…ùÚnáîê}ÂéØÜHGubØ ÞòfáòTS"Î_¼J*Öj#í[7øš? ¹{™0už*G?0ñ0üÇÏþ»ÓO(@1ù0觉ªúÕë«=\Ë$®ˆwÕΪ5[äâåk6U&OÿM>íÜҦ̪(EáäÔ©S ”ººë“Ö­[ËÈ‘# 4ŽÜÉ@y;s¦ãÉÌ[·nç"qãÆ 2dˆÀÚÊd³L˜0ÁÆW¹ùÒpo‚óuA¾å?þøcyí5Ç.{ôè¡,ÀánWpmgExêÔ©Õa…Õ?”ÛP!,Ü0„'x¯ƒÂ{ÄÇÆ 8ÇŽjsÔV:u¤{÷îÆ!¼SA‘Ý¢E UvèÐ!ÁæJ Ø‡eüàÁƒå£>ø|‡À­ ,ÏkÕª¥”^“Ù£6q®>ékõ¹sç:ªf”Á‚Þ,°ÎG_†*mÛ¶5â˜ë0MþFÀüÞl wŒ Vð˜õ$ 4î&WaXåLðý‡ Úz`m|ïÂÍ Aƒdÿþý2kÖ,)[¶¬4oî»wg} ”r*ÜåN±Ÿ$`Ì:û®§H™Zr¾ñ–}±Êß4¬ÐV-¬—B‘0nø·òó×/ɧ4ÿíº¤Mòbªçí÷°8×åäñk@Ý?9ÊÓg´ý¡×uw?ïf™1q˜æf¥žRì›Ë£*}ãz¨õyÚt™Ôeð²afÔ¥u·.÷Nè-X¹ë¾÷W,ùÕP¸›Ýɸ,?w:TÙæ soŒÅ­Á‡S îw‰«Ï¾£ú沓Z¬]À[xrÇtÌu3E2VÀö-keÁ¬IšÒ£À5‘»/¬~_Í ƒ)ý¶æú¤I‹Q:$¸Žq$PÖ9’cGBî"ð(¶ðäÎí»«dÑüm»#ÎÜÍ8ú¿ÅgþıÚn׬³;—g}tëäTêÙ¹Ÿ6 ºÆé™4Õ~`vóûö‡½š‡[³Â½Q½Ê·?æ§þR½AGå>ák7vü9 ¹Å«YµŒÇíGô„ÚÿÌØa_(w9>’•šR›½àó3°y·ã÷6ûúÎòÍU7îzwÝÉ Xª½Öþ7·inz xÓþ%óp  +ÜaÝ® ”ï 0ÄŽîeÊ”‘Ò¥KËÆ5·‰!+ôóí÷ $Pn ¹=€•ü–-[”Ûw¬ “%K¦,ë)ÛqmôÖ•°°†½½ÀŠýÝwßUí ø¨Ùzù’%KJçÎÊfg_èðY ÅtíÚµ•2ŠsX䛃¨Â Þp¹`/° ‡;–jÕªÙX¤bÒ©W¯^réÒ¥0Ç1ÆråÊÙ¼M˜0¡â¬+Ûõ“ªW¯.›7o\]¸p¡Ów3øƒ‡Û LŒ$NœX«wX±â|L~8XÕc‚À`Rϸç¢Â]'þž ÷ð± €Â<Ì´Ù׈ ^Já3A!zUWíyò`5÷U?ï‰äR—ØáBÕë¹ÚçÕ,¶¤rÝŸKÕƒ±Ï'ʯ¿ÿ%/»\äª=OŽmß¼Ö¨žFs?Á˜ÿý/ÔÚ:{®¼6?JŒ\$ÊU®£•ýXüükû-á5ÍŸw2±q'S£‘‹Ba"åéÓЗ}³œÐZŽSÞ‹ã–£¿Ôlý ¶Xmž$KžÊa•©Ò:,w§päà/dä/ª°¨=GÍß|fyó­B*jçÖµãz‚÷U'Á½;þý÷?£Z’¤‰ ¿éF¡ƒDr“µ¼ù0,̽-ø®1[»çÒ\j¼ -»÷'ùF 誵uÖ¯` šêlœQQ^»ZYé¨ùoòÏSeåݨ®ç ÷¬Y2È_~•Ÿÿ(“¦Ï—{÷Út®Z>ïù‘ÔÐb øZš}PCŠ~KÚvþBÖoÚ©¬Ýõ>ÀÝKü¹eÔ~ò¦ÖÇÈJNmu@Á·óȶ!JÓñ_“ªK†Ûì]-6ÁÍß‘Lš¾€ ÷ç`à›#1»ˆqt –•+W::äQÜÏ„w-|^Jó={öÜ®œ>}Zù‡BÁ¡Æïg79 Å9|cƒO{¬ À1lPüë¿Ð¬<áÓÜ‘À] ¬ÀázáúõëêúèG† \¾W7iÒD)ó·mÛ&ÇŽS oŒ¾æuE6ܸ#à k}øž‡õ9,ÿá.n&àÓ=UªTòÆo„éƺfóÉY\tãÎ]`&`þ6ÿî÷ç>»Û·ŸþYàž bž@Åwš.øÎÃd›½`¢Î^àz¬oß¾ªM¬ÈiÕª•}õ½å>Vÿ¸š s" ÄùÓ‰pH€HÀÞ~˜Åϸ:”þ£-›t¥¬Î”%‡QÿüÙ“FÚQâü™Ð㙲„øíÍ ¹BÑåÊÅsz2Ì–¾çÏ…X<Âu‚ÚËÛ…Þ“‘“+—,UÞÏ©ù󽧬ËGhJÌ.½¾±¯îÕüQ-Hç¶M«6‹½_^¥añè'…,cå_‹_¹ÇY±ôW5‰°Js+Sª| Ù³s³º&Ò¤Ëh\ÛU?f ¼…ï|ÈeyÂD!þ]íÏÛ±eü÷¿Å\Þ· +ßó‘‹ý5ü%Ÿ!S¨õPábeäÛaS#ܵ/DL1¸w×VCÙŽÿ¹C§‚›­ÆœM‚ñ¾FøvYòÄL™C]ÕüIíž²Á¬1">Éë¨=”á3Ÿ>c:9®Y%CFN¢ò*ãÆŸ$Ø\ ~tâÿ’â˜ÝØœI„ñåñ—iÎêü2ñÁæJbÅzE~ú¾· ý®—œ:}^ö<&±5HYµÿLOŽ6Õ,±¹’ +¦»:,¯gÍ ÿ»ëÚ}E­«OTËÌ9)ÇOž•ŒéÓH®YÆ,Ð/xú€c%¸~ÜÑÞlõ\·Fy—íëçÏY°\i1 Éìù¿+¦àK nø.ƒ¥>¶ˆ¬Øár[dß§mŠ-XÒcó†À}ŽîBÇí± ªpÇDž£ é0hª¸Ò'Vá6 «gtÁw&Û°Š.±à‚Jÿ¾|øð¡šHƒû°W´ßvpG¥ X•(QB­‚ /ĆÀŠ'] ã™6mš±Âu)î ÂÝ}V¬I$à‚€½ÂÝü sqšÓC{þ ]Öœ4yJ—Êv4bvŸ±W;~Å)ïܺ)×.7®«+ê3šçp¥%¹#«ëîí ’ý;T¸7oÛM ÜWm5÷8ú~¬êO=HJW¨!oäØÕˆ‹?×®\’/z´5jÀ¹zfF>c¦l†Â}Ÿ6†t&×:F%-±dþ íA²¤¸jí&6~¬<€Â²bé<¥Òï½»ÁRÕÉÚ0×îkW.vèfhÓºÒæƒ ê¬X¿çŠJ{c,z?üiŸA»Gºìß³]Ml˜Ýú1Lœèÿ#ð±^Œý?XÙ`/˜s&¼¯ÎȰܞ@¦¬¡t{wíwúy?®¹žÙ¥¹Ž€¼®ùFϧYÉúJ2k}Ôî»wîuªpÿmîbyò8dÕNÍúUm¾7£¢¯°j‡u{x Öí‘}Ÿ‘/c,™3¥S›/¯ëεð#–ìÞ°fwt½š"Suiݼ®žt¹Ÿ¬Y±;“Ûwî)ë÷Z€[ €€ù½AÿdŸÁÉ“'ò§n^Ý£»­êر£ ˜Œ‰AX¥CÁŽ•=ðÝ>jÔ(y³ÀbS è4\Œa5ŒîÎ A§¯^½ªªÃe–9°²¹ ¦ˆ˜ù›ã¶XJ$`aö3óƒÎ,° ?â[™?+tYìy÷FAPÏBEK©KÁ‚ꇯº ‚™}<°—æ,Ä}L¶œoJÎç>_Ïþ†`ƒ<|p_ù7Ÿ‹4ü—ëÊväß/S;—RO °ª+CѯÏ4×2OøMtÙ“ƒ›BI^³l^Ù·{›Q³U‡žò’æÛR(Ëu3l€§ž×÷³¦Ž’žšH¿O[˂ٓÂ(pL!;´‰‰y3'¨4ò嫸÷ãY ý1Î=m´\ÖµÚË„‘ßEÅKU2,5½1£a'b˜¬Mh‚Í’%[.»ÈYÍÇ=î½àóÙ¾yUupŸÜÖ›råÒ£9s_õB|ækŸ7gbÕûêŒG —›'|îkA½)¯k.&à¦rZSÆÍV±öàþù°a{éÑùsµy»á§FÝÐïùŸ£MÆ>sÊ´‰³¤sÛžª³gÌ·ùÞŒ ~+_-=:}¦Ž ‚Í;Æhÿ¾áhÜ,óW®^×\yÜ—Óg.(·5z¯ŠÌ+ùóåÒ³N÷PÒoÚ¶Ûéq˜ìà{Ãå ³ê/üÉ™ç-£•y3ÇKÊTi´•5µñääñC2müO‚ º\¿zIõ¾äá‚Æª÷Uçá¯û¾&Ãq«{ó~Ÿ!°ì†$3ùL߸n«ö1]R¦†;­ ’ÕÍ@¥Î.ŠïǾzJýªÍT•ž]úÉéSg¥lÅR’$i"Ù¶y§ü:ó79ö‚:ž=çë×3¾”r•JIá¢ïÈ–ÛµÏÿi©V¶|Ò³ƒäΛK &|Y6¬Ý,ãFL6ºÔª}3#„·ùI‡Ÿ¨ï_› 9ɼôòKNް˜¢žÀGÊ艳m.ô‚–ûq@›2g™)¿,tvÈ(_¥z½pñФIíý8ÆE˜  f=„½ŽÂßÑ¢E Áæ-R~Øá‚[®\¹l¡ÂbÝ‘À•ÌØ±c•<âB F”ïˆ ‘!C›UŽÎg™c/:.f) xJà6'˜t6´ ,²±…'°Zÿü›‘’.c–ðªªã™³æPÊíŸuTJh(±ÙË‹šÒ¸KÏo4Åî»6‡ y_Úwí§âx8Ï™>Fm6•´LZÍË—ƒÆÙ;Í¿žã iÖ¦›Lùª3qÔ÷ʵL®çÖõNO´; ùÝ;6Ù•Úf¡˜î­M<ÔiÔÚöÀó\¿ïFKÇ5”OùSÇK×6Ž­Ò?þô+¥vÔHÕZ•ÂÝ|ÌSw2ú¹à¨÷çòÅsÒ»s3ýÍþS-Ø&NÌⱘÛóUºð{eäŒ6QY¿z™Úà. wHÂÅ¥Ã'ýÕç÷Ÿlö’Z›9u‰×_€J•«.ã3«&e°cÈÀÞj3_]1)òD‹V Ÿòïf‘A#~‘ Uë©jV¼¯f>þ˜~üè±`sGÌÁ{ß)”_^Ö”¶ÿhA#=|$ý{ TM|ñmïH+ÜÑP¡w HWM=ôû‘J‰½ùV!™µd›4mÖzuÚuù\&Ï]#pía/h¯I«.2{Év‰ûZ<ûÃ.óí:÷5,â¡Ìò–kŒ VÎ7í»3e;:˜>cV™»l§4jÞQù˜·ï4Üì û«´îØÛþ‘‡zspOøº/QºŠqÜ“ú3gé©ßô#‡ýÁ}÷Ëj¥‚}»Þ‹}›¾ÈwèÚ_s§ÓÈ&.ýÿK[íó2ù×µÿìöòJ¬XêþÍøm“¤H™Æþp¤óølšºTàÂÇ^0¡ƒøßlÕ¡—ýa#oÅûj >È)R%—ŸÆÒb>Øv´ÿÌFfØwo's–L‘lšv{yE †Ø¬u#™¿|†fY=¬µ ’KVÏUýˆ7¶}%gîì2zÊOÒ¡k؉Noñ;{úœ4­ÓFîyèÖÇQð­0ðóÍí9%@ 4¨SQöï"½?i-SÇ ”Ã;—H˦µÝÍšõÛäìùKnÕ<ã7·ê± @ð0¿£Úë(‚ô¡?xAûÚš¥úSïØˆ]£F £§›6m’"EŠy_':d,ºvíZ¤.?räHiß¾½f±W[´]lß0–OÀíÀùí«ø<à pqNó…:mFÉòz.²Ò]Û£‡öÊëW$múÌJY 7Á"øú?ö”;¼Ob½[/múLÑ6<ÕŸ3'åè‘}Ws_¥>¿š_šœuÎßÆâ¬Ÿöå7ÿ¾.Ï´ÏiÂDIlüí›ë©Ï¡vàº%Uš *P¯£€¾æs¼•FL¬„xôø¡ P-éPºërR;vôàI®)þ«À|L¯÷5_æWUl…™ ')÷!zß¹·%ðøñ¹ù÷M‰'¶ÄOßö —r÷ï=#‡ŽÊÕ+×%MÚTšk›LÚg/µùfðy>wæ¼>xL^Kû¿Hf2ÂÙU"ÃoÀçƒdâèiÊ5™³ö•gy=“¬ÚêÊQo•µ×\Ý,ým¹Ô©^NæLâ­få·%«¤FÃö6­˜!E Ù®x224š¶é©Y®‡oá®xãŠéòn¡·ô¬Ïö¹Þ©*‡´ÀÎkÖ¬‘%Jøìº¼ €kø ‹ß²ýúõ“þýû»®Ì£AE`÷îÝòÖ[!σ„ ÊÍ›7ƒj|L@x¨é0â¼]e'I€üž”þ&°ìË®Ylc‹ˆ@©ùÖ;E#rj@œEv:Í=6ÕÍ}».„Ì}ö·±˜ûæ*(qRW‡Õ1õ9´sîI^ª·NØœ Ü8as%V¼¯®xò±Xšµy*Í{T ”ëoô½ÒÌÝ1áóœ>c:µ¹{Ž^/2üú|Ù]Z}ÔL”uÖÔ¹rQó…»;ãdà"p_ NîûH$@þDÀ¬‹ ÂÝŸîŒõúB…»õî9GLQBÀü03?ä¢äbl”"HàÈ¡½òqËšòìéSí‡y̶ÂÓH€H z ̘4ÛèÀËšR½NƒF>mºÔÒý³ÎÒ¹G{9vä„QÎ úµ+ 6{6jštê1Ð(ž=y°¤IÂÈ3A$@$@ `ÖE˜u¤C¾&@—2¾&Îë‘@°y˜éæHA:V+0 \ºpVÚ5©$îßSˆ“ ÷À¼“ì5 X›À‘CÇd×ν„ UËIÂD Œ¼žx饗$×9ôlÀî_¶ïì8 €o Páî[Þ¼šsT¸;gÃ#$@$`~ÈE° žF^%pçÖMióA¹~õ²Ñn *Ü L “çØtöƒæumòÁž±™àöÁr|¾[(˜  pAÀü¼à;ƒ P<å¨prļ Xƒ€9ˆ™ù!gÑs”þLàñ£GÒ¾yU9}ò¨M7cÆà#Ð3$@~OàჇ²`Nh°Ô,Ù2KBùý¾ßì x›•(Þ&ÊöH€H 8˜u|VÇ= ÔQPÛ¨wŽý&?#À‡™ŸÝvGø÷ߥ{‡†²ç¯-aˆÐÂ=  ø9Åó—û÷½lÔÔZÖíÆÀ™    ¨pw…EÑB€ ÷hÁ΋’@ð0+Ü͹à)GH|ÖQÖ¬µ5÷>ÜÍ4˜&Ó'‡K}%Ö+R³~Õ@èv¤úÈwŠHá š“ù9š[É @”0?/Ì:Š(½('^tPÆ" °!pïÞ]9uü°M™}>²uùßÿ$Üúz]îI ªÌš:ZæLã´ùÛ7ÿæçÔ)køßÚ—–&Ï_’ÇNZkðmÀ8~ä¤ìßsÐèoñ’Eµ¸×ÕfFcâþÝûêêwïÝ—ÃG½÷tþbhì \àôÙ‹’ØAØh:/íW®Ý°¹Ê±“g便b)ºåÉ“¢» ¼> €‰î&LF+*Ü£?/NþMàôéÓªƒ×®ks»ÝÙž<–ª%ݯïvìH^$0yì`ÁF!@·}ô$÷$à÷V,ûS°ù›üñç&ÉY J”u«IëžQÖ6¥«´ð«ÎÞ¹sǯúÃÎ €U PánÕ;ïã¦Kÿ»'ì ø —_~ÙoúÂŽ €? ›:¼+ì € PánÅ»îŸc¦…»ÞöŠü‚@êÔ©U?ÊÖ¨-&…úuÔ¹o:·““ǪC±ãÆ•uhéãˆË¢–ÀÁ¿¶KÛ*¥äñÃð—™wÿþ'©ÛºCÔvˆ­“ €Ì›8Z¾íÚÞh©ÇÃ¥v‹vF>˜–/‘®õ«C»l­ä+RÌÈ3a sÆAÝ?6»hß)I™.½‘®DýB¹åä‘ÃW{÷¥ D?*Ü£ÿ°!háÎO €× ˜r^oœ ’€çN—Îu«¸¥lG1bÆtÒ‹I€HÀ¿ÌŸ2¡^ÅŠ[*ÔýÀ¿:èÃÞ0šaûÑ¥ønéG7ƒ]! ?&`~^ðÁo”ºF…»n2‡H¾!tP]ë…|sI^…žøûÚUù¸V¹ý÷ ·™Páî6*V$ˆFvn“cû÷=(_§¡Äyí5#Ï X‘•(V¼ë3 „O€ ÷ð±†oPáîμ =þð ú[ì·|xÿ¾fÙ^Y.ž òënGcÆ …»»¬XH ú˜­ÛÑ‹šÍ[G_gxe   ðcf…»w“]³*Ü-p“9Dð³Â9_ç5@àÙÓ§òiãÚrdÏ.ÐÂÝcd<HÀÇîݾ-+æ‡ÆPÉ‘7¿`³’ðÂJwÛùXù9pΆGH€H€B Ø?/ÌzŠÐZL‘@Ô Â=êó $` æ™ýCÎ8Èh!pãêIš2•¼òê«_Ÿ w‘ñ X6{šÜ_úp·äg "MZé7r¢,?zIz %9ß*àv/b2hªÛ¬X‘H z˜ÝÉÀo{¹Ú ¢§#¼* D3{J4w‡—' ðSöÏ *ÜýôFY [T¸[à&sˆ$à æ™ýCÎ×ç5¬M n¼xR³Yk™²z«ÌÜ´GR¤I.Z¸‡‹ˆH€¢‘Àž-åÔ‘CF*Ôý@^ÇÈ3AV&ð? 5ô°2ŽH€HÀ–€½.¬§°­É D-*Ü£–/['˰y½@ wËÜx?hÜxñåêÅóFÏ$Ij¤Í M5Ó`šHÀߘ­ÛÑ7«º“±ÿáìo÷‰ý!  ðöï 6z ÿé&{bT¸[à&sˆ$àkö9__Ÿ×³6E3&Ùøøý|øxY¸÷¤´ü´¯$OÆ€C w$@~Fàέ›òçÂ_^½Q dÍÇÈ[:aragi<ß--vÃ9\ ˆ ûçîÉÓ"M€ ÷H#d$@ `~Ù?äHˆ|Eà¿ÿþ“Å3&—Kœ<…)SAR¥Ï mz÷—EûOËO¿.•’UkF(ЪÑ0$@$…–̘,ÿÈ‚÷ÞÒȶ¯]%WΟ3º\µQs1G#†)]^mF%&H€HÀÏ,˜<ÎèÑk Héu<$@$@$@$@Ž Páî˜ K}O€ wß3çI 8 ˜,ìrÁ9`ŽÊ ü6u‚M·ª|ÐÌ&ÏLÔX³x<{öÔéE%I&Ùò䓸ñã;­n\¹,»·lP]ˆ—0‘,Q::»ãÖµ}Í<¹Ò*í\¿FΞ8fô¨Rý&ëÕW¼åŒ c¹[îpÀü8ÄÂB  [öºÚòaÎw¨p÷k^‰‚š€ùAfÿ êsp~CàöÍ¿eÝÒ…FÞ*Z\ÒfÊb䙈z_|Ô\Ü»î…ÒdÌ,9òæ—Ÿ~&™sä ·¾¯*Ù»Kz7o .‡þ\ëÿ w_3DF¾úüxë:öÁRk4kå­¦ƒ¢óûFP ˆƒˆ~"„'‘ @а×Eðyô·ÜoHî~{kØ1,62Z!ÖÍ ’Þ.›9Už= µ®®Þ¤EŒ,ø†qáôIY¹`Ž4+UHþ˜7+øè‡#"s?¼)ºtëÆuY³dq$oᢒ){N#Ï X•À ò‚U‡Îq“ x@€ w`±j” …{”âeã$`f…»ýCÎ:8Òè$°hú$ãòpYò~•šFž ß(Q¹ºxï}ua|?\¹p^N=¬m‡äÒÙ3ªüñÇòY‹FráÔ iÑý3ßwÒîŠ)Ò¦—Z¶U¥)Ó¥³;êÿY_0tFþ~MŸh3qXëÃ6þÞeö¢…€ù½3Z:À‹’ ø%{]Ÿ~y›,Ñ)*Ü-q›9Hˆzæ™ýC.ê¯Î+XÀÛää჆ uYÛç±A"úù4Ëܺ­;8ìüŽÕ±¥Ü»}[Ÿ0h€TÑÜ&K•Úa}_fÉ™[zá«Ëyý:¾`茼Ý‹ â9º`òx£ÅøZ’Uky«&øNaÕ;Ïq“ €çìßÌz Ï[ã$qt)qv<“HÀ û‡œ“j,&¯°–Zµñ‡^k› yŸÀûUjÈŒõ»äµ TãOÿùG¦ ýÞå…?z${·m–uË ÜnD· ?˜èY1¶ê—'}ŠŽ±D„9£¯mX+KgM“k—.ú{dù¸êóÃû÷eÏ–²ê·¹òçÂyêb¥…?Èöµ«äâ™SFWª|Ð\^~å#ÏDþp¶æ'ï–Ö¼ï5 xJÀþyÁ÷O ²¾·Páî-’l‡¬N@³ÌÓÅþ!§—sOQAàу²RSzê’íÍ|’]Û(þM eºôR³Y¨»Œß¦Œ“»·o…éô™ãG¥eù÷¤DšøÒ²\1éÖ°†”Í’BjåÏ.sÇ´©óú5)”øe) ¦Ú¶i LGËúBI^1ê]x®äÜøÇR£¬I‰w*‡vï”Æ% HñÔñ¤yé"ÒçÆª_岦”Mêȱý{ž‡BOÆâ´‘HyÕ<™Œñ#&7jäË*m«”’þm›Éþí[Ä£Èrׇ>îôí߸zEÝ«²YSH« Å¥W³úÒ³i]u+äH#ƒ{¢&ô¾DÇ~ÞÄ16—e°TÌ€ *Plp0C$@$𜀽.‚Ï ~4¢‹îÑEž×% #ÀYÝÐ,ŒaµªK5Z·ë(ü~_§ÕGFÿyòDùx7 ´¬›hÊí½[7É¿ÿþk>$çN—ï»u”o»¶—gÏž©c‰’&“‚%ËõþüíW#mNløc‰üûüœ7 ’42™;MÏ;\Z”-*Göì’ÿþûϦ¾W/š/–)"ûwlµ9†Œ§c Ó€— Âc®_fóªåÒ£qm¹qå²^ätï îÞàã¬Ïˆмtaµá‰f±o/÷ïÜ‘™#‡J×úUåÉãÇö‡}’çõÚ‡.okñÒeΪg¹'   pƒîn@bŸ wŸ`æEH ø Ø(Ü_x!øÌú EÓ&}û…òµy&ü›@òÔiäÕ8q« p§ñfÁ"*}áôIéÛª± #F iÒéS)U½¶ÄKPvmZ'Ã>ï¡\ËÌ›8ZâÄ‹'ûTçU¬ÿl^ù»JÃW|GH̘1U^ÿ³vÉozR*Ôkd¤]%`Ù>èÓNF•ŠõKý¶%~ÂIJuÍ Y8u¢ÚµC)l»Ô­" ÷’8¯½¦êGt,ÆÅ¼˜pÅÜ|ðÅ$GÒ”©$_‘÷´q&’äiÒÊí¿o˜«éÈp÷g}F@å+çÏ©¾æÈ›_:øA2fË¡ò='–/‘qß~©ŽíX·Z–Ï™!Õš´0Æå«ÄÂil&•,5”¼ýçÐ#LY‰?VºÛ+ @DÜ»wOΟ?‘Sƒêœû&C, ìðáÃ?~ü £/ƒß_Ù²e>#N› ÷ˆ³ã™$@&ÿº”1á`ÒGN=,û47º”ªVÛð ®—qïß'K!P¸BÌþ«‡~Ö])ÛQþùˆ R©A$•¤JŸAò)&õ ½¡ÜsÇÐòÝ•R¸xÅj;n\µêÊáÝ›Ö ¬…uoXBCb¾ø¢”©QW?ärÿs¿žÆñzm:J·ï†ùšÍZË{ªJõ¼YÔwnÝ”mkVj/kª:‹q/'œ17_æ¬æÊlú¯&Eôcp)ãH"ÃÝ[|œõíëÒ¨CyëÝ÷T6a’¤üåsgeÃï‹UÙ¶5«|®pÇj s ¬(Q©ºÞeîíØLðÛcÖ:ø9°Î½æHI€Â'e{Ú´i厶jbK H‘cÛRæÜ!P¿~}™9s¦;UYÇ*Ü@a €çÌ?|8 ê9?ž1 MÖíhîd"Æ1:ÏÒ­Àч;7oª®ÀȺ¥ U:sÎÜ6ÊvU¨ý˜š¢{ÖèaÊBJ÷–Ÿö•X¯¾*˜xY'IòÒùëAš¢=Äoü½;·ÕáÈŒÅܾ7ÓŽ˜Û·ËöÏGNT<í9ÊG”»7ù8ësòÔi.Ïý³ä~»¤ÎÑ(ë§3:e‹6¤[à£U5—_z):»Äk“€ÿàêIÿ»'ì €ß€e»®lO”(‘ßô‹ LOµXN˜ÄÙ·o_`ÀOzM…»ŸÜvƒžƒ¦ü- ´ ¨ã²YÓŒn§Ö°o-nä™ ·n\7:šBsY9yø€©,òán˜¯%H`äŽ}!¬XïCá¾fñ|ùô‡ŸË"!«573º¸ëNæì±#ú)’){.‰«¹°q$µ[´lf‰ìXÌmy+툹}ÛØ %º'îÞäã¬Ï¥ªÕ’óf©¡عMj¾õºäÈ÷¶ä/VBr½U@}o$H”Ø“¡zµ.ƒ¥z'³ Ó{§U†Ìq’ @x lßµkWxÕxœ\زe‹4hÐÀe Ÿîá3b  7ÐÂÝ H¬âUë´ƒfÅaµ&ÒÇœW G}cøÞøûÚUãB˜4œ:rÐ(Û´b™` OîÞ¾eT"¾Ê¯^¼ 7¯_“Ý›7H~m2ÁU7j>»!p;70îÈÙÇŒji= dÙ±öRÂsûæáãÜS‰woòqÖg¸öÁêƒQ_÷U.ˆàÂåà_ÛÕ†1bUV>ÍEчÝzKÁ÷Ëx:ìHÕÇgÔüù.¤ý5[ßGªñ 9™«æ‚äFr$@$@$@$`!T¸[èfs¨$à; šê;ÖÖ½’9X*¬—+7hj]:ò½[7É¿š\]áŽ`ºÀŸu2Myž$M‘ʨ]ù:dÊÐïTÙŸ ç)…û®ëDWÌ¿_¥¦ÛÜO?2ÚŽ7$ªQN"²c §y;cnßÙ ‹ý1gùˆp÷&W}nÔ¡«”«ÓPE…ÛŸý;¶Èm©,“»4_ÿØz!µ>lëlˆ^/_8u‚`@KÕI8ß›cÆ8¯Å#ÁF€/ÁvG9  nT¸÷ýåèHÀgháî3Ô¼FV¡[þüÃ`ñnÙŠÿÍ”À"ðËÈÐÀ£É5w2™²çTHŸùuc °8þrìT#ïn¢bý …ûêEóTÓµK~3N¯P·¡‘/‘>k6£Ê• 猴}Á7õÀ¯IR¤” ¯goŒÅþ:‘É;cnߦî‚Ǿ<¼¼§Ü½É'¼>ÃÏþ?QžY'Pß#`rãÊe5´AŸv’Jõ›H¬Ø±Ãj¤c²á·©ãvð™)Z¾²‘g‚HÀ9ó{§óZ9ºw·ü}õŠò¿viˆÂ=±¦x}û½’FÝð²f7ªüµq­²Œ6ÕöiÑP³œÞª²]Q¸›”õ‹Þ~d÷®˜G¶mý|O¹{ã^ë×v´üè‘L6H‚…l³®ÚçìÅ•™,¹ÞlU5“ʹ3¨`ºXqqh÷NyëÝ÷5çÕ2¸7º~ù’ÑfµÆ-Tߌ&H€Ü&ðôŸäÒ¹3òJ¬WÕê%ì_ÑâP8zv¸Ý(+’ @ PáAp<HÀ–€YáÎe¿¶l˜ó.|ÖMŸd4š8Yr…;Åÿ <¼_Î?"3†1‚X¢×ð·^¥Qsc™rä’lyòÊÑ}{äÜÉã²X»ßö“*h«kýªšEùiuÞðËóõ‚xBáù¹_OC¹Y¶V}‰3¦^-ܽ®˜=qp¿àº“+íû °9~âue;èþá½5›‹yq—¹M†[ÕîQÍ_”“.LÊÀOºY$N"šŠ•3_­–1KÅs³zÓ–æn1ýœß)øQð>/½ü²,9M&ýø 0”Cñn£ˆ®ŒWeÚj›cÏë¾òj,£¼lÍú7~|›v™!   W¨pwE‡ÇHà9S§NÉøñ¡Ë¾Í`Ž ¦‡òÑ£GË’%!Aùôzõë×—pK´lÖ45Œ¾­K£]äíbïKªôåÂé“J!¯+ÛS¦K/i3eñtÈ׿töŒl]½Â8“†)4·J7˜Þ7ܨÍ*AJÀlè¡ñ£¾_Ékšb|Øç=ô"å;¶ûwîež$ĸzÓVžœÂº$@$@$@$ T¸óC@nH•*•Œ9Rî¸ñ²>mZÈz½Ù—^zIºvíªgƒvoþáC…{ÐÞf¿ØÂ)lúQ­ñ‡6yfüƒŠØ\Iž‚EäÛɳZç/Z\Úôî/ã¾ýR¹”™:ô{Áf/©Òg¡s–8´~Äꇂš5óæ•¿§Á¯zö¼oywo¿÷¾´îÕOõßwó'Q›ýùi2f–φ³)öÆXlt’‰,s'Íz\ì)÷¨æóÉ·Cî|Î?*·ÿ¾!#¾èãpLqâÅ“¯ÆÚ>ÃVôB!Üû˜Ÿ›5›·öB«l‚‚—€»ï–?î&qãÅ—]ÚÙüE”LÍæmè–&¢ðx X˜@ C'· ÄŠK`¥©\¹²$I’$"§Ô9fŦù ¨¾³³Càέ›¢ûáF¯ó)&é2g œX¼§pÝwÍ5?ÚßM+c–¬v¨l×1µü´¯Œ]¶VàŸÝ^^Ѿ—ëµé(VlR.iìëyñ4‹'ÁRÍç!ݪÇç2féɬ¹¼±—˜š_ð†í»ÈÔ5Û5eO<ûÃⱄiÔO™»Ñ¤[U<å•|â%H(£µÏZƒvCÅꊲ5ëÉøß×Ë›…Þuk|‘©ôìéSÍ-ÖD£  ¦[,$à›÷N»3j4k%_O˜!ø^ŽŒ¼üÊ+R•“ú‘AÈsI€H€HÀ²"÷bYl¸ 4kÖLÆŒãñÐqžÄüÃÇ]+$+pá½Kà÷ÙÓÕÒp½U{¿Þz9÷ÑC`íùÛ^¿0 ³6ïUJOÜ'ׯ\Rn\ÈÔQàRû”ÓüµcsGŠ–«$;nÿë²*&yfmÙ§úsüÀ^å<µfÕŽ‰ŸØqãº<7²cqÔ¸7™/ÚwÊÑ%lÊÜa„<á®_ "|Üé3ÚO¢ÊE ÛvŸ}¥â\9V øN¦ÅÀgéµ ônDù~ÝÒ…ò÷µ«ÆuªkAŸáZ‡â„'ñ€a±+˜D‹÷5éѤŽ}T¿ÐRç)OôÎ[ €#ZA D%…Sí‚¥6a°Ô¨äͶI€¼O`ÁdÛ€º –ê}Æl‘ì Pö¸ß×IûêeåúåKö‡æŸþóüÜ¿—Œþ¦ŸÀµL–í$÷ÛÖe! @(S§NÉ¡C‡B ìR/jñ5²fÍ*3ftË¥^xí¡yü%J”HÒ¤I#)S¦ôÈèñòåËò×_½Ì“'¤K—ÎÈ;J?~<Œçƒüùó«k;ªo.»sçŽlذÁ\$2dÜ¹ÃÆ¬²©ÄLÀ Â=àn;R§N-eÊ”‘?þø#ÜnäË—Oðem1[ÑÂÝ*wÝwã|ü𡬘7˸`ÖÜy$GÞüFž  ðwPà-š>ÉèfªôTa£€ ‡^bwÆj…‘ôåŸ1[¿|ƒRº_8}Òmzø¿]6kšÚ ¸¯Óò#)«Åá ;·²" €Å¬^½Z¾þúëpG;vlÉ•+—”,YRÚ´iãTùîn{úÑnóæÍ¥uëÖ?~|½Øé~øðá2cÆ ãxåÊ•e®dÙ²e2dÈ›*¸f¿~ýlÊe,X ýû÷·9Ô´iS*ÜmˆG†.e‚ã>r>$à®Õº»õ|Øõ(½”Yá®M1G鵨¸õ¬\0ÇÆÿ*‚ RH€H ¬^4OîÜüÛèrf­”E–QÀ„[lÞ7Ü:ƒ•‚’@VVb’kÜòõ’9§k+œo½-ñ& ƒíÈž]òU‡–R1GúYw‹( @Ä+ÖtŸ@ì¸qeèœ%ò^Ū6'ÕÖ‚£ÚKÎ|oK¿‘eÙ‘ òñ—ßIê ™ì«ÈÕ«W¥W¯^’6mZiܸ±lݺ5L „€u1¶jÕÊ(œ5k–ú>5 Ÿua°T÷$q6ïoF½³÷=Yê´j/µ>l#1b„ÿóÊÿwËT!³IºL!îgâÅ‹¦»wï––-[JêÔ©•›ø+¦ ØH T­ºÚHWJÛÖr?§ûpÇpñ•0aXW¤X1û믿V«VM¥õ=2pãÊZÝ8YKÔ¨QÃÈÂÿ»#Y¼x±Ql®o2t£º!s@$àX.êè¥Ü™"Þ;WõßV¼õÃÇGÈžEs°Tü¿UnØ,:ºÁkúY£‡É¤¿Q=y‘ w?¸#ì‚»Ìîd0YT¹Q3wOe= €ýÓAäA»Î_MŸ†¥äÔ©S¥`Á‚aÚ¸­M¸ 2D­Ž-W®œRâüûï¿aê±€H€¬J K–,ÆÐáê%¢‚‰Íáǧ#¶ž#Ù¸q£áj&Mš4’?~U ûT©R©4ÜÑÀEŒ;R¦L‰ó<ˆ6,Üq®½è w¸ŸqÖ/ûs˜lÖ3à ìûÅÞûX«à‹8tÉ—/ŸäÉ“GÏZjoV¸GŲ_KÁä`k—.Ê–U¡/9…µ@fÉR¥& X>÷ù±gcä/½D w~MàÄÁý²o{ˆPt´TµÚ’ QˆP¿î¸uŽï~t3¢±+¾øÀÅLDåem2 Æ8ØvíÚ¥ÿÍœ9S=zd4‰wå+V¨-]ºtÒ¦Mew” °2ó÷àÅ‹“’º_t3¸í8p ¹H¥ñ]»aÃå¶E?˜5kVéÖ­›žµÙ›ÝÅT©RÅXAg òð-AðÔêÕ«Ûœë(óꫯJùòåeÞ¼yÊ-Îï¿ÿ.õêÕ3ª=zTôUNp?£ûŠ7*0”¨pÊÛÊAùЬÙÍ ÷`µn¿¯ÍО:rÈ%ÖGîÇÿyü$ÜúFe&HÀ X…b¹Ÿ.…J–áçJ‡a¡ýž-åûnmFüô~ÇØaÆo Lü­Mß •*Ëï1"ág.?kSéò¹³dhCÄL›åÂé“ò¢/ºåMùc/ºïö~øA&Mš$£F’'NØTƒgŸ>}ä‹/¾Úµk+ÆEб©Ã X…€nŽñBÙ_èªj/OŸ>5”áöÇÌù† ÊgŸ}&±cÇ6«4V­\¹Ò(7»‘A!òºÂ}óæÍrþüyÛ8ÁIŠy(Ü!pGcV¸ëÖí8Fw2 ` ¡ÂÝ÷™£Œ"øRŃàÎ;‚ˆØøb&Ñ­‰6ÿ¹B6ÿù†ÛC;uä Ô+ä~}·fEK0[8[/»6­çw ?I ÛfÙoê4'SH G“:~Ag6w ~ƒ»ví*]ºtQVí#GŽ”%K–Ø @à/¿ü¢¶7ß|S)Þñ{¬|2·É4 #›7oÂ’Ü‘²Ý¨àF–éðH€€¬öëøî…dË–M²gÏnS%gΜ7úD)\†á»<<Á¤)|Æ_¿~]¶lÙ"×®]ÝrÑ¢Eêt|·ÃKŨp·Æ}æ(£ˆ"Yׯ__Í€ÂW’$I¢èJÑÓl©R¥<4ÂÌcÆ‚ ±Ž‚“„ד€N/A˜ÈÒËôâÆ«g¹·gÏž ¾WÌîªôa¿¢-ûw N?Î= ø,o†…–.ø‘åÈÒJ?νcöÏüïã;€b-\wïÞ=cвcL™2þõÊxønÇvöìYõ»aüøñaÞ¯÷îÝ+­[·–îÝ» VͶk×N)ƒµÉ2 &PNë—[ÎïRýõW˜ÃÐWÀeË÷ß/GŽüŽ4häÎ[Š/nSÊx]£’–€ XÇãû¿_ dGàì}ûö‰î—ngð»–b T¸[ã>s”QH/ÄXr„}°É믿.îF ‡/2øMƒ ØÈÖ­[ƒ ÇãC5RÖ^ú%wïÞÍ: ìñRúî»ïÊ­[·ŽK4§L™âð IÀ_¼óÎ;²cÇÕü¸ºpá‚@IHñŒÀÚµkåý÷ß7Nš5k–T¨PÁÈ3a ð‡n^IºzõjAì¤@“ôéÓË7ß|#ýû÷XMŽ1BYBšÇƒƒŸ~úI† &0~ù裤jÕªý›ÏcšH€•,Âuq¥pÇ&Œí%mÚ´ÊíKáÂ…Õ;Õ+WT¸x1+Ü8 ‡…ºÊÅ»6WréÒ%AUè;Âx@€Â·2P¸ÓLxÔ‚÷xŒàGF¾!P¨P!)V¬˜T¬XÑ7 €«8ZVÝfý„”¬óçÏ7zS´hQ*Û ÁŸ¸q㆔-[ÖåË/‚ÓQHÀŸ `’PW¶£Ÿ˜$¢²Ý;wÌѪï´ÌVü™@°½[â9ãøÆ÷”2ö+`ðY_µj•Ô¬YS2dÈ _ýµ\½zÕŸoûF$@8|ø°ú.ÔO,Q¢„žôx³ôS§NÙ´a–ŠïÜT©R9ÝÌŠ}óy6 Úe`Qw4ýû÷ËÉ“'•+1äá^†±:@Â:B…»uî5G…`u7*Vóà`ûQdåûcŸ1c†`é¸.øJ±¸ßÀäåÑ£G]˜ w—xxÐŒ=Ú¦mÚ´±É3C$9æ÷ÎȵýgçÍ›WÆ'/^”!C†V˜Ú ,0ûöí«,84h ¬-íë0O$@F“ˆ½{÷6º 8IGFräÈaœ®[º£¿/au® ‚ªbÒÓÙÖ­[7½ª ²êlå­Qéy¬ðÿüóÏåòåËêV*ňA¬=¯`ÎónóÝåØ|F9¬.ÁôÃÇê÷2ºÇ?a£ ðÕ[§ŽF3:Ç„W ÀO3^PÍVÁÎ.@…»32,÷ð3 ˆºäÉ“G°ŽB$@®` üÃÿðŠ+® àØ,ˆ—·JX]‹ «pk Ä Ðœ?^¼>ͱÊG¸ÐŠì{¾yE!‚±êz |¯Þ½{W] 1@*Uª¤_Öá rÝ¿SÐ_w~ÜuÙ´i“žTßéF† K ÂÝ·™ƒ$¨' ?Èp%ýÁõWå‚‚àìٳǂÛ/±624þûï?iܸ±Z:ïΠ0ÑJ¬]°JÇ,µmÛ¶ÑÕ• ¸.ß)‚â6FzVú`¬eÊ”‘ ÈéÓ§¥OŸ>Ê=DâÃ÷ ,B?þøc¥¨·¯Ã< D'…ź¾AÉ3gN5iˆ F³Õ8Œ¬à‚/²ƒ-]ðãöíÛ*k– ·5ñãÇ׫9ÜÛ»€1Ÿïð„ç…iÒ¤Äñ1KÖ¬YUWsÓÁO€ ÷à¿Ç! ø„î>ÁôуÌè¥;Dpï;vì(î¾Ä‚Dd-_‚›&GÝ`qªKœ8q”Ÿf=Ï}ä ˜ß7"ß[TVù  |·Ã+gPÜ^`±ùóÏ? Ü( È*‚>{ö̾ó$@$àsPxïܹӨ<(=²éÞëLzРAËóÈŠ½"}ûöí*6”ÙÚÜl…îêzæzð5¿ìîˆÙ­ êcÅÅz¨p·Þ=çˆI J˜øXÉ )J`Z´Q¼|!‚.o¼ñ†(P@Ïr¤ú÷ï/#GŽôhtT¸{„‹•}H?êÌ«tàkÙliåîðR$T¬þn‰çžî»}ïÞ½ÒºukÁ„ž½¬^½ZY’"Èê—_~)fÿÅöu™' è WYÙ³gWnC¿øâ Y¶l™4lØÐk]ɘ1£ÀÊ\LVb"R×W໳téÒúa—û *ˆye­»ÁS“Êü{… w—˜ƒö µ£<ímåÀHÀ÷ô®lõE¾§Wüõ×_åÎ;Æ`hÝn ÚĈ#/ÚžŠùÖÓsYŸ¢’€}°Tº“‰JÚlÛÊÌïV〸XIóý÷ßË”)SdÔ¨QaÜÉ k¿~ý”u<,-Û·o/ï½÷žÕPq¼$@Ñ@¿á¼ù;."ímܸ1ÌÈ;uê¦,¼‚×^{MŽ=¦ÚrÕ¬ì;æ%`þáC…»OÑÍÅÌîd`IðÁÍØ8°`u7tèаÜ(¡ÂÝ H¬âs˜04[>åÏŸ_°Q"G€ï‘ãdzƒ—:ðÝ7«V­’š5k: ² —mÅ‹¬„rÞcÂ:ð)ÿí·ßªmòäÉžœÊº$@$@$`9T¸[î–sÀ$õøã8êÛŽ?.ëׯ7†k¬D‰y&‚@Š)”åÇŠ+¤V­Zòâ‹î/º£Â=ø>Á0¢iÓ¦ ‚ƒéBëv„w÷æ ~ï¶ÌÖü™ß-]ßÝwû™3g¤oß¾‚g¬½8p@>úè#dÕk¾ž³üôéÓ¥W¯^j>|¸³j,'  ÐPáÎ €W˜óG‘WZª‘ &ØŒ·E‹6yf‚“¾+Ê”)#p'„€p pËß5îÁùyôQ­Y³Æü¶Ãß2…H j˜ß;£æ Ù*üÃwû¹sçdÖ¬YR¬X±0¹wïžtèÐÁf‚0L% DŠî‘ÂÇ“I€tfkäĉëÅÜ“@¸ž={¦|êè –Zk€5Þ'Ÿ|"/½ôR¸§Â=\D¬ J–,i\µsçΘ  ($€gi½zõÔêÁ}ûö VÜÄ׸"ÞÛÍ.øŽkÞ¼¹ÚªV­ê¢&‘ ¸¿~›¬H€HÀX¦^ºtI¹…èß¿¿‹š}ä÷ß_¶l™Pá$7<‡Q¤H‘ • ‰®Düë~DWoø9ðœ<\]µk×Îó}pƵk×dÿþý’1cFÉ”)S¸W|úô©š8¸zõªdÍšUÒ¦M+x‡¤ €¿àSÉ_îûA$@$pùòe¥@Õ‡^®\9õ£IÏsoGŽ‘uëÖFà\(Û!ø]©R%Y´h‘œ>}ZÞyç£$@$@ÁO€îÑs1Ñ7o^ÈnhÌ›»†6°Œ‡Ý|nëÖ­Õ€,X o¾ù¦$Ož\J—.-™3g¼ ž:uÊá€wíÚ¥üÒÃ=Nž}º ˆ¾|ùò0§â8”ú/^‡U{·nÝdæÌ™C Èž={¤GƹµjÕø|ß°aƒ Ø:Òþù§$HÀ¨ƒD¯^½Ô{&ƒ½Ú`a†H€HÀGèRÆG y  [æ`©8Bw2¶|¬”3KE@TεÒÝçXI€HÀsœxñœ™¿‘$I¥Œ7+ñÓ§O/ƒ ²é*ÞÿùçUeûܹsm&åa9_²dI{¬š£€U àeéÒ¥V>Çí%ÇŽóRKÖn† wkߎž¢”ÀÉ“'ÕK±~‘æÍ›« Fz^ß=zTV¯^-wîÜÑ‹¤sçΆßF£ÐK Xʬ_¿^>|¨Z„‚ÏÕrÓ¨`Ž7n”Ý»wKêÔ©%W®\òÁ0xcïÉíÛ·eÞ¼yÆÙEŠ‘9ry&¬CàСC‚ÿ-]ðc?Â)$`uð›ŒïÉòåËKÍ¥…H€‚…Þùà«=Mš4áiÛ¶mFØ(ÛZ¢páÂR¹reÁª9 XÀ‹/†¨öîß¿/\Õaµ»uãåêóȱ¥Â=rüx6 € GŽQË9õ*°`Iž<¹žUû .HîܹÃ@BÀÄX±bÙÔõFfÇŽa”äqãÆuªpGp&,u5[Ré#FŒ¶mÛÊ?ü ð=IqŸÀ/¿ü"=2N u»Âr ³u;ï,XªåÀpÀ–'ðÉ'ŸÈ´iÓ¤P¡B²eËËò +ËÞz›ós`ƒ#à3ùòåsKÙŽž={V7f̘’)S&—cÏŸ??î. ñ`°xýõ×¥]»v²oß¾`"ÇåcP¶ôÑG>¾jp]Ž ÷ຟ ¼<ûòG”9`“+XP°üñÇ2|øp§Õþûï?9r¤`?jÔ(§õx ,ñãÇ…¯½öšÔ«WÏÈ3a˜t™:uª1àlÙ²IñâÅ<$`e›7oVÃÇDñÓ§Oãâù‡Á<nåχÕÇÎÏu> P.2àCÁV+V¬ètð8N!«ÀïR €ÿ`ÐTÿ¹ì ,…}Kœ8q˜1¦L™R` ?nÜ8#8R˜J^,€åËÁƒ•‚\_zç¨y¼°ëÊv(é±4oïÞ½W(p1Ó©S'Ñ•÷£GVËbµÃ²°à–›.P¶Ç‰GÏro!sæÌQÿSúiÝ®“àžDž¼ç¡:uêç" ´¹ÝdÉ’I™2elÞ ôÊøÎûý÷ß8Pp±…8J 4Pç`*•í:-îI€\˜={¶¬]»V­°wUÇHÀháî ŠlƒH@(BðPX™¸ÝÅU;qℌ;Vùw†Ò]— HõêÕeðàÁ’0aB½8ÌþáÇһwoApN³Õ<|Æ×ªUKúôéæsÜÄÀb½páÂE‰#å£.%|ø·<ºÐº]'a½½Ùº£§;ë}üaÄ PVèz_`ñäÈê Ï!¬t‚²iëÖ­òòË/ë§{¸ÊÂD¬½Â  ˆ‡;2l„ŠUTf7 z#ß~û­lß¾]Ï{ó÷¦^ úéÓ§+¥»•VXѲYÿXkωßÞï›7oÊ_|áò¢ößKK—.•9r¨«ú‰hÃü¾¬—Ãe–î6K/ƒq ïx÷¶L€"®Þß¡0sô=!Cu}(ç)$@$àˆVË@°Ç;…¢’-Ü£’.Û& 8p lÛ¶M pµÙ¿œ;CkÖ4+ÛQçã ‹Ã?þøÃáéèÜÙüôÓO6ÊvT†å:üFc9ª+KE¸Ár³ìÙ³;¼ÆØµkWãX‘"EŒ4Î ˜ƒ¥Bae^%àü, 6˜ƒ¢PXÐ9R>êǹ'¨"—aPÒØ btäÎ[YdÂ’\(ÕáRÌ^nܸ!U«VµQ¶ÇWY_–+WÎfâöôéÓª] X›ÜRmÙ²EA€ñƒ¾¢ÝÚT8ú¨$@ ÷¨¤Ë¶IÀBDJwX±8ú„`FžˆÞ|ßæË—O)DðÜ·oŸ²¼|ù²Ô¬YSöìÙ#Y³f5š†oÇ*UªŠv¼Àc?z/^¸~yðàÃ>„“€’Ö„º5?~”Pq4íðÉ“'eݺuFE¬Tp÷‡œqAAn<àCQZ·ë$¸÷5-ZHéÒ¥Õ3F¿væÌ™eþüù’'O½H¹/èСƒÊÊóÓO?5Ž!ѰaC¹pá‚*‹;¶šØ…¢]÷ûŽu°œÇ «»wïÊ•+WÔ¤-ü›¥Q£F‚ E¿Þ&ž[xRH€HÀW†.Ø"#}ô‘`ó¦à½…H€ïm›™ùîÞÝÙ3gÎyé%ó /Dl¬d ²ŽN£¸ó.d°¨ÁGÛ¸qãœEÌÈ‘#Òw§v*¥:.J{|òz_‘³Ï>»s°Øb‹•\f³d$XªH¡Í(º$ÍI t'Ó®];ç£9I¨ÕµF`ÑE5¼+Be;uä]ƒ…9â[݆ý7eÊ÷ÑÆ63w°´Ü~ûí#e;û±þD±ÿÀDŠsfk%Ë"­$N@®Dâ<šuK÷A³^yµ[D@Ê'€ñVí¡Ð“ˆ@5 ȽštU¶ˆ@ɰZ6l˜ÁR0¦›âJ†©þø—dZVƒ(2ð›{ß}÷¹äXz_’a~”)øßxã œðX¾u‚·2 þÞ{﹤(PØn»íòemúã Š„VK-µ”»fM¦ 0+%ôQ½ë®»æŒÇЄˆÔä6$€UyÚ*îaxæóÁöÃ?ÄjÈ@p((çsI8ðȬ«W\1WrKù%i³‰è>h¢‹­¦Š€ˆ@™ˆág ú¢î¿ÿ~ç®–q¨Y¸WƒªÊ(›A–’Êv_hÇŽcþÓQX ~Éúþûïïüಞ”&ÅI!P~Ú½²«\\ôêÕ+Y¼¶S0xÂÔ=/\o-ê÷iÙBëvZ,w2Íqݹ•BõBÜÜÅäú â&M""Ÿ€,Üó3jÖƒ 2¸ðÂÍLèõúë¯wq.0À‘ˆ€474kvâ¹áæR"Õ" ÷j‘U¹" eèÒ¥KÎüáq,f¿d½k×®,²Êºë®køx+ÄB »(gΜéÊ[`̃>è¬ä³ž@bp'ãE;i%ÍGà»ï¾3|{áãxà 7ô›ZŠ@]Xzé¥K®7þÜ%" Å(¤ÿV|©ÊQopeI8IÁÅ$ýuf¨âæK""Мˆ…»¿4A衇¦Ò>(›€,ÜËF¨D@ªA ­ãž'œÒ?ÿüó»C~Éн\ˆv¾5\œtÒIÎZÞ+Û—[n9ƒOy\ÒH #€¥gh]´ÕV[\ÊHšÀwÜa¾ýöÛ¨á²nPh¥Ž ¬³Î:Qíq[öÎ;ï¸àÞ(‚òý]qÅQÞ\+ùÞW¹òÖû1Y6×ûTýE zL½×^{fž¦É¼óÎë‚Z§Ó>æ 0xð`÷šŒ3ÆL˜0!íö‰@Ù¤p/¡ ¨ï‹=[Ùáq¯ìðKò˜.—dåöyPØ÷èÑÃ\xá…~—³Ä=z´Yi¥•¢}ZÉOßí(¼(Xª'Ñ|ËÐ .£øH–ˆ@½ ¦ˆ/|òÉ'¦{÷îæ‹/¾p>ßqa–ücòá‡v¹é¡[5‚†'…Ç!C†˜>}ú˜K.¹$y¸a·s1kØF«anV¢0ˆ@ë®»ÎLŸ>Ý|üñÇ-þx‡nfÒòkŸˆ@cHs'¶8ßñ0­ÖE r)S -¥h5çž{®Ye•U CÁêüòË/pÎ1ÇfÓM7uI6Ø`3ß|ó¦è?öØcæÔSO5ýû÷7¤ eøðá1ðá1ÖñÓŽÂ$ô­Û»wosë­·‚±†‚ƒ€yøwG©"iIছnŠv.´ÐBf§vжµÒ<P†JC”„ᬔæ!¡–ÖÃ?<6gܸq†àÜýúõsƒ®Ôõ©§ž2gœqFäVŒw ïóÎ;Ï ÄbaImÞAøpÇ}³x¨M@Tîs\L™2Å þðÃ#ø>ðÀ£íp×go½õ–Ûµçž{š]vÙÅ,¼ðÂæóÏ?weÌ»¤Áz‹÷A_%"Ð 4ðÒ W¹ð6Î9眦C‡…gPJ¦ 0qâD3jÔ¨œm½í¶Û º}ËçĤƒ%½hÊ""P9|0qÄæ‘Gq _2Ñ»í¶›¹øâ‹Ízë­ç:Ñï¿ÿ¾yæ™gœ2ç;ûì³Í +¬à6ÿûß›«¯¾Úì½÷ÞneP6ß|sƒ]”¯¼òŠ+Ãçg‰5û2Ë,cºuëf°ÀÅz>T¶“† ©iŠŒ3fþhC¡®(¯YdäÈ‘‘ˆ6sm’ Í¢ÙÛZ·ÃBîdšýŽhûö£Ìæ îÃPº3XË,'„gû³Ï>%ã½õôÓO»¼>ÁÚk¯mhúöíë| 5ßL* Ìõq‡K3ÞG¸Pc øÒK/ê\á=¹øâ‹'wk[D@D@D@š–3­óÉG}äŒõ¶Ùf›|Iu\Š" …{Q¸”XD ÒPl$á9°ç/M°æ;á„b‡pQ1vìXså•Wºý“'O6aÀÎXâ`cêԩ΂=iQï“0%5—`)iI É^îdZ2j†=ømÇ»,ˆQJD - 0X»ûî»›aÆjy!VÇa‡æ7:~ÙÇïöáS|Í5×4þóŸ£4¬ì»ï¾n€÷è£6÷ÜsOìX¸ÁL¬Í6ÛÌ`µÞ«W¯ðPls :ÔsÌ1-IÈà%ñDöØcwnR7¢È‡{#^ÕâÛ¤û xfÊ!" ÍL€™ñX¯"¸•‘½RJS )Ü‹¡¥´" '°úê«»ið>úh45ž“àfРAExR˜ªÅa6×$ëÙ³§S˜0µ?)‹,²ˆ9øàƒÍ\¹ÀW.îüMš4)™%ïv¾ ­y hÀ(°þõ¯E-Ãݪ«®mk¥yð[#²nožk_Ë-E‡+˜|‚kþ fBÝ}÷Ýî}F.þx.±Än–ÇyªŠ™±µòÊ+›N:ÜÙ4›È•H³]ñôöê>H碽" " ¿ÀõÞ|PŽûï¿ß|õÕW¦}ûö¥W"(„@óõÒ ¡¢4" %FŸ}öÙce3þ®»îŠíó¸‹Áç3 püߢ\@É€û—|–|ø×}ã7 nhPº¿ýöÛîŠåb—.]œeà9çœãO[y䑆?Iy°hÆÝŽY·{Í· ÝÉÌ3Ï<β·ù(¨ÅÍD+vfqTb&Êù®]»º¿fb¨¶Š@H ÐAª0ÖE@D@š—@1ÁPÑWàŠöÐCm^`jyÅ Há^q¤*PD I ´r^gu’‡³n£Œ/GaÁÇÖðüm½õÖYÏ£Õ!º“AÉJLIóÀź^pá1ï¼óúM-E@D@D h²p/™2ˆ€ˆ@Ó`va!±tB (è¥p‰h½\éì±\*Ê/"P>}øá‡ ÁK½l²É&~UË&€o~\ xÁ7>JwIó­Ûi½ÜÉ4ß= ‹@9dÙ\=åæ#0xð`β/„À˜1cœ+ÀBÒ*B@î…PR¼xàsýõ×›)S¦Diñ5íСƒó×%ÐJíÛiäذmUòÀº„é™^Ö]w]ßDD@J% ËæRÉÕw> ¼Ô÷õSíE@D 5 ãN&¬ùÎ?ÿüp—ÖE dR¸—ŒNE@Bø=jœ¬Û#M·º“Áo;þÛ%" " " " " "P ß|ó9çœsZݯ_?ó믿ºýƒm·Ýv-Ò°ãóÏ?7‹.ºhê1íbHá^ -¥ÈJà¯ý«á%ºi×®™þù³æÑÆ$pà 7D ›}öÙÍÞ{ïmk¥y`Y2~üø¨Á{î¹§üøG4´""P(¹)”Tc§Ó}ÐØ×W­JèÚµ«á/)gŸ}v¤p_o½õÌé§ŸžL¢m¨()Ü+ŠS…‰@s@¹ŠŸvIóx÷Ýwë O`Çw4 /¼°ßÔ²‰„Öí4[îdšèâ«©"PEr%RE¸uT´îƒ:ºXªªˆ€ˆ€4!Yš°Íj²ˆ€ˆ@•ÜtÓM&ü–;™*®ñb¿úê+s×]wEµÄŠd­µÖжµ"" " Å…{1´”VD@D@D ­ HáÞÖW@ç!ðË/¿"»{YrÉ%ÍŸþô'¿©e¸õÖ[Í?üµXÖí ­ˆ€ˆ€T€@8¸_âT„ˆ€ˆ€ˆ€T”€îÅ©ÂD@D y <úè£æÃ?Œì·ß~f–Yôš‰€4ÑJèN†8}úôi¢Ö«©" •$ ËæJÒTY" " " " ­A@šÖ ¬sˆ€ˆ@ƒ¥¢ Aá.i>¯¼òŠyóÍ7£†ïµ×^fî¹ç޶µ"" åes9ôê7¯^ê÷Ú©æ" " "ÐŒ¤poÆ«®6‹€ˆ@… |ýõ×fèСQ©[n¹¥Yf™e¢m­4'f8zè¡ÍÙxµZD@D j4ðR5´*XD@D@D f«@*BD@D É ̘1ÃüüóÏ…ƒ:(Z×JsX{íµÍe—]fxà³çž{šÕV[­¹¨µMGÅߘ1cÌóÏ?oþóŸÿ¸ö/¸à‚æàƒ.˜ÅĉÍ“O>i¼ôrôÑG›¹æšËoj)" " " " "P'¤p¯“ ¥jŠ€ˆ@-èØ±£¹ä’K ¾»±nïÕ«W-WWu«2£Ž:Êð'f ÀàÒÎ;ïkê +¬P°Âýƒ>pSá %…lX wcäJ$vk5í†îƒ¦½ôj¸ˆ€ˆ€Ô%)Üëò²©Ò" "P{°ÆäO"" ÍD`ÖYg-«¹ä—2±p„r%R8«FN©û ‘¯®Ú&" " õO@>Üëÿª" " " " mD`‡v0/½ô’¹ð Kª3„&L˜`®¿þz³ð —T†2‰@£РT£_aµOD@D@‹€îu=ÕV&°Î:ë˜<°ä³.·Ür.ÿ ,PrÊ(ÍD@îÍtµÕV¨?r)S×L5¦&ðÙgŸ™o¼1bлwoÓ©S§h[+"P™3gš)S¦˜É“'›víÚ™%–XÂ,»ì²åYV^±Nš4Éàë}ÅW4K/½tYåÕ[fY6×ÛS}E@D@D@D@¤p×= " uH`âĉæÉ'Ÿ4_ýuT{ü§—`K±±cÇšçŸÞüôÓOQ™¬ ÈD¡Y/òù矛áÇ›÷Þ{/ªòŽ;îhVYe•h[+õM`Ú´iæ”SN‰±Új«IáÑÐJ©ÞyçÓ¿sçwš_~ù%V AP»tébÆg.¿ür:– ÇÆqÇgî¿ÿ~óå—_F©–Yfsøá‡›ý÷ß?Ú—\¡>4·Þz«Aéî¥}ûö.H+ª›Ñ"^–ÍþNh®¥^šëz«µ" " "Pï¤p¯÷+¨ú‹€4¬Q0þüóϱ¶rÈ!%+܇jPJ§É¶Ûn[W ÷­¶Úʼú꫱¦,µÔRR¸ÇˆhCD $À3°G-ž«>ÍÛo¿møC®»îº‚îXÊ£OÊôéÓM¿~ýr*Ü×_}“¦\þꫯÌÍ7ßl}ôQóüÃl½õÖÉâµ- O í·ÑðVE@D@D@ꆀîus©TQøÀ¬³Îj*mé5Ûló:€¤± `Õ»ß~ûE\rÉ%£u­ˆ@±˜Õ³Ç{Ä”íøTÇ/û?þh^{íµØŒ\Í*sÏ=·™sÎ9c3‡f™eƒ…ûQG•³¯PdæÒÚk¯íò0»‰ú0àúñÇ›ž={ºF,ð%" " " " " µA q4,µÁSµªèر£™0a‚s)sê©§üY—+X±¿üòË‘Kܱà6¡å±Ç3O<ñ„¹âŠ+Ì3Ï~kÇ{¬9ýôÓÝ.f(m¹å–þ°–"ÐpdáÞp—T †& …{C_^5ND ™ Ð8p ¹õÖ[c ÉöíÛ›wÞÙîÃú»ÚòÉ'Ÿ˜ /¼ÐÕãóÏ?îøƒSšöéÓ'¶?m£JÞd¹cÆŒ1¸áÁ…n"ü+£L»öÚkÍ /œÌR·Û• y饗šÓN;-²ÔMy÷ÝwÍùçŸíF ¸úê«GÛ¬T¢.÷Þ{¯á/M’ƒii´Or¸å–[œòúúë¯ù[gÀŸíüÝ~ûíΕ î¶x®*_ýµó³^¬Â½K—.9OOŠÎ™QE Ƚ.¢š " "  L`–n›š&" ME`ýõ×77ÜpCLÙ€¯¾úÊù^uÕU‚jByôÑGÍk¬á”ûIe;ç}öÙg•h=ÌôéÓ³V%/JÛ‡z¨…E5™¼’—óÝ|óÍΗqÖ‚(ÕŸ|òIóÅ_¸óS‡iÓ¦™»ï¾Û<ðÀAÊú^ÍW;;ì°ƒYj©¥b Ì7 tüV#íÚµ3ÜgXúâÖ‚àIÁ%G(•ªËŽ;îh¶Øb WΛvîð¼ZbÌ?ÿüæÊ+¯t3r¸ïû÷ïï’®²Ê*nPΗŀ榛nj~ùå¿+ër±ÅsÇHË쌓O>Ù£$ ­äÓNâ—£þ*²lnÔ+«v‰€ˆ€ˆ€ˆ@ãhù¥Ü¸mUËD@D ¡ xEÎ\sÍe6ÜpC§]gu¢à|¸äèÙ³§I*D+åÅ_tn>ýôSW$ Q| ãnU¨ºï¾ûÜ~oeÖ¡JÞ°¼p×IÁ?óV[me¶Ûn»ä¡ºÝN$°.3† â\[ `ç @Gp™‘L=ôÐhw÷îÝݽ3zôhç¾2 X0«"—T¢.”O ÔÇ܉DùÜsÏå:­Ž‰@Á¸¯ºvíê q{ÅŒ ~ýú¹¸7ÞxÃT%h*Ï „{š<¹WKüævÙe—(Ù\`öÞ{Q‚Ä ¿Ñ\çYß,âßuÍÒ^µó7xÑ " " "PO¤p¯§«¥ºŠ€ˆ@ݺu3ÿþ÷¿Ÿk|Z¿ôÒK.ªwñ1cÆ ³ï¾ûd™çT±Ã(@qƒ53‚U( ÑñãÇ»@ƒôêw.þ£…®B©”’7,“uꇥ6–ó^ø9vìXçkyøðáÎÿ¸?VïË´ ¸ÃÀÏ3÷ƒ!>äqÇg®¸âŠM&½òÈ#./ãçŸ6»í¶› ÔJ}x^½ÿþû†g«^Î>ûlÃó_"" " " "N€À÷ÇsŒéÔ©“9äCÒi¯T€î„©¢D@D - œuÖY-”í¾>(v°ð>ýôÓÝ.,+©pøá‡ý©\€Í\ŠìÃ;Ì) òŠk“‰'FJÚJ)y£Êüw%T¶ï³Ï>†ÀˆX…6²”òµ×^‹ð¬½öÚÑzÚÊòË/Ÿ3>@¹uI;§ö‰@¥àRÉ+Ý)s„ î/[ù|°… î{î¹Ç¹kÊ–žýÌ6 …@¿¸÷ºì²Ë 3“®¾úêðplý…^p3•b;ÿ»Ñ»wos '¤j˜}~VTÃ4H )‰€îƒ’°)“ˆ€ˆÀ `TÁŸDZ‹€\Ê´iGD@ªL K—.9ÏõÕWs¦-ö AL½l°Á~5u‰Å'Öš^€¥(yÓÜ¡ø²Ó–¸¹Á½M£K¹A Ãi—ß~ûmY¸Ê­KY'WfÈC€XXœ”7é6)ÌJ¬‡çŸÞ ^†û±RÇ¥K®Ag™?üðCtäÖ[oí“Np=„ :ÔùåfYáóX}’JÔÅ—¥¥Tƒ@= J<¬ÑDÄM÷3®e˜Å’;Mp›ä+iÇÙÇfþ² Šó4Á] õÁe3u˜Û&¬ò›MÑžÆGûš‡€,Ü›çZ«¥" " "ÐdáÞWQmKkÇ\'f%%t9rÉ%—8Åv¶òGŽiFícJ€U/¡u—³•QOû+2ôQ}Æg8Ÿ÷Ù, ‰ €êXÀpÀ1T•¨K¬@mˆ@  Ôc6Ю»îêîé<Ðl¾ùæY•íU¬Š+šØÌbðŒà«XÓ3 e{µÉ«üZ'í}TëõVýD@D@D@šƒ€,Ü›ã:«•" M@€€¤(¯Q…ò믿:ŸéøÑFPrãþ ’‚’ò|öÙgåôµ×^k]tÑØizè!³ß~û‚"XX£¤õ’TòbÍÙ³gO“fÙ†’—`žøcÇ-Ã7Þè‹i±Di…»‚ n·Ýv."ùqAàÕ^½zµÈSÏ;¼?êRƒ@Òv{\ŸÁƒ»ë #,k™€Û ,¸ $>¦á‰`©Ë5ñ ÁJÔWCXìcéKKÄßC¬c­O°^/‹,²ˆÙc=\<¿OKú$öü¯Ï–¨Ö" " " " ÍB@ ÷f¹Òj§ˆ@ÃÀª kb\¢ ìô‚)ý°ˆ$péûï¿ï”Ìÿþ÷¿}2ƒ‹‚0àß믿nN>ùdóâ‹/¦*3Ÿx≘2ÅfŸ>}Ì™gž•‰býoû›9þøãݾ{ï½×<ùä“f›m¶qn[¦OŸîÊÇÿ±’ù+Ù@IDATêGžP*¡äEñ~Í5ט7ß|3,Ú­¯±ÆæÁ4ÞÂÿÇt¸œYsÍ5Ý A¨ôoQ@ì(7$ÍDÉuóÍ7ÜÿŒ5ʵœû(¼—ÒpœvÚi‘²ã•¨ ×tذai§sûXøüóÏ£ã¬sb<ï¼óFûµ""PÿdÙ\ÿ×°”hà¥jÊ#" ÍEàÃ?4Çwn}Ë÷Úk/«Ço7Ê’oô|ÃâÞPR{¤p¯½k¢‰€ˆ@NøÛ¾úꫳ¦ÁÚ˜¿4!0ß 'œ;tÓM7,ϳIš2³ÿþN™ún'(à§Ÿ~j.ºè"çŸéXG§ ‚»ï¾»Eç§J^¾ø^Î&XI‡‚òßÈøGñÞ w‚@2àйsg3aÂgѶٯcáÏ̬ÖÓd®¹ærÖ˜K/½4k9³Í6› üØ·o_³å–[ÆŠªD]°VGéϵcÆF>ñîR¶ç#¥ã" "PŸ4ðRŸ×Mµj FU×®]ÍÇ;nD—Xb‰Ø¾FØàÛìÄOt3ʧM›ÖMj¸6HáÞp—T ht«¯¾ºAqŽo¾ù&jî©§žj äÜ|D;ÿ»B°¿+®¸Âì´ÓNÉCÎ7ðsÏ=ç,Ð Qfz ÷PÙî ½à‚ œË\½à«=ùA¼à‚ š}öÙÇ °¤úü,ËUòbeå•W:Ëì°\¿¾í¶ÛšÍ6ÛÌ`mÿÓO?¹Ý(ú±~ÇÝM#H¹A CóÌ3a0…Yo¼ñ†ãŠÕ;÷JÇŽÝŠ}”ÜiR‰º¬¿þúfìØ±iÅkŸˆ€ˆ€ˆ€ˆ€ˆ@“à{λ´l|k#š½ýöÛ±ìÍÐþzh£îõp•TGà?Ùi‚»¬€'Mšä¬¶ñŽßmÜzdë€` 0f̘´âJÚ×­[7óÔSO9ÅÿË/¿ìÜÚ ´]vÙe] R|Èç“r”¼(ܽk›´óàw7Í"t> É_9Âu#8n ·Øò*U—bÏ«ô" õK€ç†DtèÈE€·|3k#°ñãÇçJ^÷Ç~øá‡¨ áz´S+mN@ ÷6¿ª€ˆ€TŽÊxü·ó×Ö²ôÒKþÊ‘J(yË9¿òŠ€ˆ€Ôä̩ڪjÓZt´iGD@ê‡qÅöÜsO纴‘îï¼óŽÁMçï~÷;çÆ39kî¼ÝsÏ=fôèÑfï½÷v³“ëçÊ5fM¥poÌëªV‰€ˆ€ˆ€ˆ€ˆ€ˆ@C…{C\F5BD N|ÿý÷.ÔôéÓûHfK ‹k”ÄÄ÷B0ÂÂÅi!3“ˆÊ­‹/ïƒ>puj×®s%ʲùå—_\þ÷ßßÅÉjÍ@¥7Üpƒyâ‰'\uxà§|Yn²É&N^løË_þb°ú—´YÚîÔ:³ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€G@îÅñRjÈG€øLW]u•Yn¹åœrÓ[o½µSN/´ÐBfçw6'Ÿ|²!¶Òˆ#Z÷ᇚ“N:ɬ³Î:f¾ùæsù¶ÜrKÃß +¬`(ã”SN1_~ùe‹¼ÉåÖ%,ï¾ûîs®5QÇkƒ 6píÃÝæ›o¾&M]' 1ÀpyºÒJ+™­¶ÚÊ ×l»í¶«¨kÖÔ Ø; ŽêÃçÇô›1e;ÔÄ%“²=ÂÓf+îh3ô:±ˆ€ˆ€ˆ€ˆ€ˆ@.²lÎEGÇD@D@D |ï¾û®Ùk¯½Ì¨Q£R ûÏþcî¿ÿ~÷Gó(¯CéÛ·ot<Üï׿ýö[sþùç»XZüœsÎ9ý¡Ø²u ¼ä’KÂM·Žµ:þÞ7ÜpC…¤0°ËÂ¥—^jpÙ’”Ï?ÿÜ<üðÃæ‘G1|°cR-%÷Fmdà‚uûÝwßm† ff̘«Òꫯnzõêez÷îíb¸Åj£MȽM°ë¤" " " " " Åes±Ä#½^ã:ª" µGeò®»îS¶ãBf‡v0Ý»w7¿ÿýï[Túí·ßn±o­µÖŠöWlùå—7Ûo¿½ó'Žrž}ÈØ±cÍ•W^¥ W*U—°L¿N;°HßxãÍ,³ü¦ ýæ›oœÕ¾O. ¼zÑEÅ”í+®¸¢c…¥»ú% 0_|±ßU•åÜsÏmúôéãæšk®ç@Ù~æ™gJÙÞ‚LÛí…{Û±×™E@D@D@D@D@D@Š$ —")¹ˆ€d!ð·¿ýÍY{s¸}ûöæòË/7ûì³O”šçíĉ;ûîë”åÑÄJÿþý‚Àž(ÛCã$EÉ~ä‘Gº\Ï?ÿ|"÷o›•ªK²ð3Î8Ã)£½:¾å;wîl¾úê+3nÜ83mÚ4Ó¡C‡(ø§Ÿ~z´Ý³gOsÍ5×Äܺ|öÙgæ˜cŽ1ƒ réÎ:ë,Ó£GWn”Ñ®ôë×Ïàn§P9à€œå}Zz\É0Á»åÁM–öÈÙgŸíÜ÷` /© R¸×ÆuP-D@D@D@D@D@D@D@D@D Õ„.Wn¼ñFƒr9faÑ}Ûm·™UVYÅàŽeå•W“Dë]»v5¸Ÿyã7œ ”É“';_â(³Q{yï½÷üjlYɺø‚)Åx(‹,²ˆ³à¿ýöÛ  O=õ”³÷inºé&×N¶ÿô§?™;Å>Üo¾ùfƒ«\½àS¥·WÀû²î½÷^CÓB…¨¸ºIÎ÷ôÓO»CX¼30€BÈ!®¾‡rˆyíµ×Œf…¥Ñký}R¸·>sQD@D@D@D@D úh,R$Ñ}ÐYMhuS§N5_ýµ;ïÚk¯ÝBÙVw*X ã³ü¨£Ž ¹uñ·Ür‹ÁÒ;›B½E¦`G%ë릆Û~}±Åó«NimØ•^x!Ú|ñÅÍÒK/m'WfΜíÂ/|5åñÇŠgÆtõ2eŠyýõ×ÍÇl¶E´Òê¤pouä:¡ˆ€ˆ€ˆ€ˆ€ˆ@)äJ¤j—G÷Aã]SµHD õ ` í…{>9ñÄ IAÙ¾ãŽ;º`žÉc…nWª.…ž/[:üÈO˜0!:ìÝ·D;r¬àÛK÷ÐÇúèÑ£ ®` •0o2ÏÉ'Ÿlð;ÿÇ?þÑì¿ÿþî0n€ÞzôÑG;—2R¶'©µÝ¶îmÇ^gÈC@îyé°ˆ€”@`þùçrá¥T9þøãcÊöN:™m¶ÙÆ,¹ä’NAŒówß}×<óÌ3YOQ©ºd=Aðó¾øâ‹›÷ß¿ÀÿK†bý»ï¾‹)ÜqýR)Yc5ÌСC[‡ËfHj‹€îµu=Tdណ‰€ˆ@ºtébfuVçÿe8ÖÓóÍ7_ÎÜ<ñÓ>çœsºt¸T¹á†¢<¸”9í´Ó\¹ÑN»‚›™\®Y*Q—ð|嬯³Î:‘Âý„N0çw^ÁÅÁS"˜ED@D@D@D@D@D  Ȳ¹¯Šê$" "а¾^k­µ\S¦M›fvß}wç<[Û† fV_}ugþꫯºd¸QÁªYpÁÍ©§žS¶£ 'pè.»ìâÒdûW‰ºd+»Øý½{÷޲\tÑEæÚk¯umB™žüÃÍË/¿ì‚¥~ôÑGQ>­ˆ€,Üuˆ€ˆ€ˆ€ˆ€ˆ€ÔY6×Åeªx%5ðRq¤*PD@ë®»ÎtëÖÍ̘1ù…YvÙeͶÛnk–YfÓ¡C3}útC@Në(–½ >Ü)ëWXa3Ï<ó8¥;þÎ×\sM³é¦›š…ZȼñƆ £D eܸq† ¬Ý»w7ÿûߣCåÖåÙgŸu¾Ì9¯—>}ú˜M6ÙÄüóŸÿ4¼K&OžlöØcW7Ÿæ”SN1ƒ6÷ÝwŸkËž{îiî¿ÿ~3dÈ—äˆ#Ž0W]u•Ùb‹-LÇŽ…?´ƒ,þ\èÀj–YdÛì€4ù?)Ü›üPóE@D@D@D@D@D žhफ़®–ê*"PËpå2hÐ gŽòŸ~úÉ)žsÕí(¥‘ÙgŸÝ\~ù忀pÛo¾ù¦á/—`Žÿ’K.1‡v˜¡<¤Üºœþù楗^Š«ó;ï¼ÓxàNa~õÕW™†òÅ_˜'žxÂÜ~ûíæÐCu‡ à¬ýQâ#'Ntn#Ë?ü¿ëý”NîÖ°K^t5YD@D@D@D@D@D@D@D@vÞygóüóÏ–¹fáƒýœsÎ1cÆŒqne<¹ý÷ßßÜrË-f±Åó»bK¬ÞO<ñÄØ¾XÀì½÷Þ‘²Ý,§.Ôc•UVñE¹%®j¶Ûn;³Á¸mÜÅtíÚ5f…ΠVð[o½u”—ú9ÒÜ|óÍf‘E‰ö§­¬¶Új_ïøÁ—÷4B͹OîÍyÝÕj¨y¹>ük¾òª`Åè>¨J$" ©Ö_}çkýÃ?tÖç9ýôÓO_vܨ LÇ{6w)ûì³éÕ«—SÆ¿õÖ[ÎÍ ú•VZɹá¤\pA깓;K­KÏž= ¹„²ÇŽ›+ItŒwϾûîëp3aÂ3iÒ$X÷1K,±„s#Ó®]»(VDÀÂÝ“ÐRD@D@D@D@D ¦ hªvM_žV«œîƒVC­‰€4Å_“îƒZ¾:ª›ˆ€ˆ€ˆ€îºD@D@D@D@D@D@j–€,ÜköÒ¨b" " " )¤pO¢]" " " " " " µI@îµy]T+ßHá®;AD@D@D@D@D & Ȳ¹&/‹*%" " " "ƒ€î9à舀ˆ€ˆ€ˆ€ˆ@íesí\‹Ö¬‰^Z“¶Î%" " "P.)ÜË%¨ü" " " " " " ­F@/­†Z'(€î%@SHÂ=IDÛ" " " " " 5A@®Djâ2´y%t´ù%PD@D@D@Š …{°”TD@D@D@D@D íÈ•HÛ±¯¥3ë>¨¥«¡ºˆ€ˆ€ˆ€$ Háž$¢mš! ÷š¹ªˆˆ€ˆ€ˆ@¤p/’’ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ÔY¸×ÆuP-D@D@D@Ò HážÎE{E@D@D@D@D@Ú˜€,›Ûøèô" " " " E½hdÊ " " " " "ÐdÙÜÔÛþœxiûk ˆ€ˆ€ˆ€N`¶‘#Gš#Fu^ ‡¦”"PË>ûì3W½ï¿ÿÞôïß¿–«ªº‰€ˆ€ˆ€ˆ@N_~ùeìøƒ>h>úè£Ø>m4>O>ù$ÖÈ»ï¾Û¼ýöÛ±}ÚO`ìØ±nõ©§žÒ7±‡¢¥ˆ@«øå—_f·'êÿ‹-¶XFÖVa®“ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€40ÿ»ï¾û2Çoà&ªi"Ð\°»óÎ;Í\sÍeößÿæj¼Z+" " "ÐP¾þúk3hР¨Mݺu3«®ºj´­•æ ðÅ_˜ÁƒGÝb‹-LçΣm­ˆ€ˆ@HO¯¿þºéÒ¥‹Yo½õÂCZª°î3 pýÿYW2™ªžI…‹€´*7ß|Ó}ˆ.¼ðÂæÓO?mÕsëd" " " "PI“&M2Ë/¿|Täu×]g9äh[+ÍA`üøñf5Öˆ{ë­·š½÷Þ;ÚÖŠˆ€„úöík®¹æÓ¯_?¹” Áh]D 5̰±g~§ ©­Zç(‰€‚¦–„M™D@D@D@Úˆ€îm^§(ž€&iÏL9D@D@D@Z€î­ÇZg(‚€,›‹€¥¤" " " " 5A@ ÷š¸ ª„ˆ€ˆ€ˆ€ˆ€ˆ@>²lÎG¨1kà¥1¯«Z%" " J@ ÷F½²j—ˆ€ˆ€ˆ€ˆ€ˆ€4  ¼4àEU“D@D@D HáÞ@SMh;R¸·{YD@D@D@D@D ¹É§‰é>h¢‹­¦Š€ˆ€ˆ@½.¢š " " " " Í@@®Dšá*ço£îƒüŒ”BD@D@D íHáÞvìuf<dáž‹€ˆ€ˆ€Ô)Ükêr¨2" " " " " " ¹ÈÂ=hkR¸·õÐùE@D@D@D@D@R Ȳ9‹vŠ€ˆ€ˆ€ˆ€Ô0)Ükøâ¨j" " " " " ÿ# Ëæÿ±h¦5 ¼4ÓÕV[E@D@D þ Há^ÿ×P-¦! —¦¹Ôj¨ˆ€ˆ€Ô%)Üëò²©Ò" " " " " " " " " " µF@ ÷Z»"ªˆ€ˆ€ˆ€ˆ€ˆ€# W"º  û@÷ˆ€ˆ€ˆ@=½ž®–ê*" " " " ML@®DšøâM×}ÀЪˆ€ˆ€ˆ@ͽæ.‰*$" " " " " "à ÈÂÝ“ÐRD@D@D Há^WIupdá®AD@D@D – Há^ËWGu&& Ëæ&¾øjºˆ€ˆ€ˆ€Ô))Üëô©Ú" " " " "ÐldÙÜlWü·öjà¥9¯»Z-" " õJ`¶z­¸ê-" " " " " "Ð|4ðÒ|×\-¨=ö˜yùå—Í»ï¾kÞ{ï=Ó®];³ÐB ¹¿%—\ÒtéÒŬ»îºf–Y ³ Þc=ÌÇéÞ½»9öØc£íBWÆg† bÆŒc~þùçÔlwÜq‡éСCê1í4R¸§QÑ>6'0çœsÆê0Çs͵!" " "PÛFmŽ?þxóÜsÏå­èꫯn^xá“|ÿ§e¤ÜÉ“'G‡:wî­ºòË/¿˜vÚÉL:5g–ü1çq$†’¹´-" " " " " U&°è¢‹šÍ7ßÜeÁ4Ûl³M•Ϩâk‘€\ÊÔâUQD@D ?W^yÅlºé¦)Û)müøñfèСù ®P éÓ§O¯Pi*FþG@ ÷ÿ±Ðšˆ€ˆ€ˆ€ˆ€ˆ@xôÑGͨQ£ÌÛo¿m–Zj©«ªÓ:vìhæž{îèT:uŠÖµ"" "P›Pf÷îÝÛüôÓOEUpíµ×.*}9‰÷»ß™+¯¼Òl´ÑFfÖYg-§(å¹”‰áІˆ€ˆ€ˆ€ˆ€ˆ@-˜m¶ÙÌlPKUR]Z™ ‘{ï½× 8ÐüñŒf=´r5t:"¼ôÒKfÒ¤IQüµ0Àl¼ñƦ}ûöæÓO?5ï¿ÿ¾ùàƒÜò“O>qî]–[n¹(O®•»ï¾Û„®^˜WŠì³Ï>†?ÄÇÙo¿ýÌ-·ÜRJqÊ#Ž€îºD@D@D@D@D@D@jšÀŸþô'ßDD@D >à‹=”“O>ÙôìÙ3ÚE TþJ•jXÂ{f~YjÝ”OäRF÷€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€T˜Àþóóõ×_W¸Ôôâ¾ùæCÐr÷/Ÿ}öY9EDy±X·-’ßp­Zë¾ó¶! ÷¶á®³Š€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€Ô·ÞzË 6̼öÚkæ×_jOo\“{äÚk¯5¯¿þº™2eŠS‚cɽæškšuÖYÇqÄ÷*¹ä£>rç3fŒ™9sf,)~Ñ·ß~{·ïÕW_u>ÈŸþy3qâD3çœsšõÖ[Ïœ{î¹æøC,_Ú ú‡zÈÜtÓM‹ô?þعUi×®YvÙe‹—<Ðâæå‡~0=ö˜yòÉ'ÍW_}e^~ùåØ)ÿú׿š…^8¶ ÊfS>÷q¸žûرcÍÏ?ÿÜ¢v\zé¥fH=Ö–;§M›f®¸â Ãõâ¾À•2×\s¹û¢{÷îfï½÷.+VÍ÷ßo|ðA3aÂ7“`5ÖhË&7ý¹ÿÏú'Ê´…Ï?ÿÜLž<9ï©æ˜cCœyæ™'oZ|ùå—1ŸPìKNýÈ·=ûì³›ÕW_¬y\mzøá‡Ý„ ?Å_ÜM…Ùf›mÌZk­åÊჿAÊOÊú÷¿ÿmøQ a=ýº_úã°™o¾ù\úä?Ê¡<Ÿ–¼<Äs—âáÏè%iý~2WZi%õÔ7ß|Ó¬ºêªîE†O4I~t è ¼ûî»æ½÷Þ3t.øíðGǨK—.fÝu×5³Ì¢IAùi*…ˆ€ˆ€ˆ€ˆ€ˆ€´¾}ûšk®¹Æôë×Ïôïß¿¢ùî»ïœþ å¤`Á"…p.AéL ÎÝvÛ-k2”¥ãÇO=þ·¿ýÍrÈ!æ˜cŽ1·Ýv[äw<™ø„N0^xarw´=räH³ï¾ûºAhgÊ º¢½öÚË\uÕUYõQd;ãŒ3 JõRŸï H¤ ú3ôT väÙK,±D®$ÇûÍ7ߥEŸ¹Ì2ËDÛ…®0X‚¢ý¬³Î2ß~ûmÎlèàÜ¸à‚ Ö‰úg̘a–^ziƒîÕ ×â´ÓNó›Z¶ö7ó;~˜U;E"cU{2ûÿÙH¦W¯^;B—Z?;r–±³ŒU€\f¶ówèÐ!õÉöÇ–±Ê·¼ç[e•U2ö¡•±ÊöLÇŽ3ãÆK•¹ýöÛ3‹,²HÞ²’u¶Êð̦›nš±Ó’beîºë®ûãlQžUf¬B=–– û ËXå}‹ôþ|sÏ=wæàƒn‘O;jŸÀo¼á®«}‰×~eÛ¸†vð,c¶dýøßK;(—±YòÖØ¾ˆ3W_}uÆZ$äM«" " " " " " •%pØa‡¹o<«p¯lÁ¶4kÕ^Ð÷cø-™mÝZªg¬‚¾E­R?cÀ²žÇz:]S¶rÃýÖX´Eùì°ÊØ¢õiÖâ=c-´SËc§µÒÎZç°NÙÖo¸á†¬eOŸ>=Uç•,Ë*ܳ–QÌ?ÿùϱ¶ð_¬XÈ úÁdóm¯¼òÊkÀ[Ô鮿þúçA™vU°—BÀYUWÝ\sÈ!†Q*[C{O.Lù׿þå,Ï‘Iæ:t¨aÊL8}§ðÒ‹K‰_%¦·Ø\4í#W XÍ‹s¦ãÜ{ï½-’ÿýïw™[ȳƒÑÒ§žzÊYØû¤LG¹ë®» ¾Á’ÂèֱǛÜmØbf@˜ˆÑ1ûƒ-¨½a>­‹@½xå•WŒ¼2Ï=÷\AUƺ€çN.ÁÚ¡sç΋ ¦ &Ÿ[¹òꘈ€ˆ€ˆ€ˆ€ˆ€ˆ@mÀ;Öå .¸`ÞŠ2s«÷=z|b) ú²sÎ9'ÜåÖñ”€e>žÒä‘Gqº¦ð^"¶Þzk3ï¼ó†» >Ù“‚Uüé§ŸÞBŸ6ÿüó»úî¸ãŽ©ÖÜXysì‹/¾Hé¶±¨ÇãC²©‰ƒp¢î¹,þÛ·ooÎ<óLc•ÈAÎÚ]EW¹ûî»ôƒI¡½›m¶™sù“æYÏÇ|2[ÎmkHÜâ83)fuÖûµ£uT]á^nP”ÈL—¹øâ‹cDÖ_ýV¹qPšo·ÝvÎU¬El0àkU›ÜUð6.jºví¥_a…Rý`ùÁwØa‡X\²0` Jl ^q‹L}ï¿ÿ~çźõ|&u.X1FM“?þñîØ»”þË_þKv÷Ýw»rQ܇¸Š¦îù\KcŒ‹o{˜øü;í´Sìµ²;£'žx"V;óÂcÔ‹Ÿ{®;ëÕâ¿=kíïÒ„ûr­£·Üj«­¢ÁŽßÿþ÷n€"W«.ªMµS!œÃ~¾|¸yàÜÓ'Þb‹-òúdçÅÃ$Ÿà? KúP6Ùd³ÿþû»€ø†Bi÷öÛo»vâ',é¯+-J4<PÐaë…ö„#`øâaíŸUXÛ‡£zÅ Xyù4hO-y b o]Ÿ}{î¹§cÏ?ß!FÛðÍŃY>«#„Zi  ‚Mš4)j¾Ú `ˆbäœß/qßÁ!º:¿×|bð €l>èÂtZh>IJî âkHú¤Öí¨YmµÕܲ–‰ PãêÔ©-ª‰Oèð{µEí €®d±ÅkÑó™ÏIaA-QŒA_„"6›¿vtNiÖËäE/vë­·šMíÌm/XÞc5Ÿf9OšË.»ÌÜÔ Ïôo|'…²øN¦|‚Áâqaæ7õÍ ‘|ü…º,òñ\(Å:yC }´çSÒ‡ùZk]AkCÁƒúŤ²[·nî}_èsý ƒrúAt~ù„û} çFWJ™ÙâIæ+KÇ+C ê wªÉC…?¦2„ w^ÄÙ¦Œ „>ôÐC#å1?l¦Ûx…;å@”3‚ˆ2æ…2¹1s ‘ó)܉}çwFÅPêÉ>¦û`qÏmâ‡Åˆ¢—4¥5?˜-·ÜÒýùt,Q°‡ w¦Ìò@â̇ešÂÅ#l]t‘;u"r5Hȃö0  F%@ôõPN>ùdÅÛï£ãŸDD@D@j™@£(¼pÁF¿#ï"‘õdŸ»–¯…ê&ÅÀ¸ƒþ'nH³¹HÝe—]œÅi1å¶fZÜ?¼üòË©§Ü|óÍ‹V¸£¼çû¼gÏž­2“=µâÚ) €erš²Ý¾ý‹õí¹…aàû?Méíó%—XÊãZ˜ºB… (è½`l‰Î+ßyqòâ‹/šK.¹Äg5XùÛØeѶVþG}Cè^ÝÆ}ÌpÈ%;ï¼³Ó{Z?ù.}¬£k,T¤Ë(”TõÓµŠÂ½”f0JÅÓZ¼Û:â•F‘¼„ë~K¦ð„‚ºŽ?>Ó5ä#±AcÃbÚlýÒK/u>¤dqÛf— áOÌo•)Q¼ôÓšÊ@ÙLÉÃY¹‚Åz(庾 ˪çu:Lç³Á™[X#T«]\S:åø–£ êž´ ¨VU®ˆ€Ô J+¼Úª]ÌVå#3\&Òïóh]êÀÌ™3Ýýî­[³Õ× |O£LËVV5öÓ_¬” Tb7‚!Ï6‰Ô#\2à”OPp3¸äÖ|Ë ¤Åò½éÕ«—¹ãŽ;Üw[!é}û¸MöÂ,îUW]Õoæ\b ŠžËVR?—óÄux0ià‡ë!XJ‘qãÆ¥p/åÊS•{KV¡~¼ÄC% £q¥ÈM7Ýdl°ð÷?üáÎ å¥ù3ÇÂfäÈ‘Ñéøq„S;¢)+Œ’Å;Ö9méSŠ`~d©JLaáÇrMi‚v‰@AxY_y啿ÑGu.Z>üðC÷æþbº#¹øJçcé¦üþð%Wˆp¿>ôÐC†ß/÷,ˆ¹—ù}1Mß÷s>7/œ‹)sXÍ0Ãˤ5~óÒù”Mçƒ 6ÈZeÜI1e‘g:0¡àš‰)ƒi‚O5:c0òSİv¢,ž?~ ‘¼XHØHô.ÐNZY´‰žY<ö²ÔRK¹N¾ô’ËgŸ}ÖM=z´óµ7mÚ´hš Êkâ8PG‚ߤM—L–‰;0¬5è|ñ1 ÷ÁöÛoïvÁŠû†v2Ýö3Û†ÙA<›ó õ¼âŠ+s\e1mað”`µÝ»ww>ðh©Â½Íµ›0a‚ë ¯±Æ¥¥|" "P5•TxU­’¬~i”¤a0Ù+Ûq÷‰å(}N¾u½;CúÔôukUÙÎÅ ÿ{RïR«Þ|Ö›Ù."}B/¬KáîihYop R¨ðÝãîäá[²P…;F”¥ôÞyçXõ¼ÇƒØÎ,|.¿üòðHè¢5K–¦Ý]ÉXn¸–Ô)«ti5±îX2Sôg§®ä<÷gœ¥%ŸUÖ¥¦¿ýöÛc饳ƒØ±)S¦¸cVá–±—Œ œ±A!¢ô~Å*ÊbùìƒÐ*xi}òe¬ò«àô$´Á'bçµ®`ŠÊo}SÇò[%WÆF©Ží»ð [”¹âŠ+FilÇ®Åqí¨öEí®¥U WµÒ—_~yÆú˜Œî›ð·mÝŽâT'Û c])å-Û(elp‘ŒµªÏY®uñ”·¬luf?¿«4±JÙÌ ,PVÙÖÍST´‘µ,;c c­¢´áŠu£•5õ· arWŽý°Ë™'äaâ¼:VFÚ†õá—µLòÛ ;ëú*Ãu Ë×­r?­h·ÏºÈØø-žia~¿n£½g¬/ÄŒýxÍZ^¶\Wžƒ¾,–vP&[rí6#`ãeìlÐÌßÿþw÷gc'EÏ.;øÙfõ*åļké£úg¯µp/¥åš'`ckE÷ù\Póõ-´‚6`_Ô®bŸ?VYå=餓 =¥Ò‰@V|ð>±^ ²¦©Äë8ºw9ŸUH\,ß=þÇò¶ÛnËš×WÅÒòm–VØ?ÿùÏX9ôŠ‘ðïà|b]gÅÎgÔòe)ú¸O;‡5Æ*ºŒ´ åèæúôé«Sx‹]·†ÃiÕÓ¾Ú&঑Ԕ…;SSÔ Ûán¬”¨»…–±>˜Ö§¡`ኟt¦úäš“'Àk±R >  &qÞyç™Ã?<ª>®q°ö/Ä28ʤœþùæ”SN ö¶:bÄgŸËÝ VÎLWóSÕr•lŸ±ÆvLœ¥6A™±nNž)å#Ôi®˜x^…ÁK9‡÷ÏÆs+[ Ê…VX÷'Kõ\BX|üyÁr;àÕK[bmOàj,Þ÷Øc´$njbÒb"Lˆõ=VíÌTÈ%Ä™Àª‹þP>ûì3¬'ŒqO®ã˜˜ÜsX‘ãnÆvD#—`¾\¬Oðµ*+LODKZ À”tút^°6Möaý±Z_òžå—téXëõnäúñ.åÝŽÕ5.~¬¢Å0{¶Pa†!ù™Á‡Ð`ö™/´—޾~ô­‘+£˜zø“Ñgcf3âô—Í­¨O_­%3õ¼2³Ï§M.™á‡%<î ´Êõ©e‹ødý«µÍ,KkdçÑ23–ïá´þsµÎ¯r››Ï<|›0— §bVH(Ö4ܬÊ:ê¡0 Ï …mã·å%Y–߯åon¥ÃxÖ°®d—2iz1®­9(´p·Š‹ŒýaÁi±¥þaÅšM’îÙÊ`¡#xVáÕƒ:Úw¶ÓWt9£hT$iá~Ì1Çd¬¢.c}TGíƒuc«·,Üc8êz£ÚîvêX e,–™-b]†dµq 26Cìžó¿K¬‡³‰ à’šÇ*KÝ=¼ãŽ;fµ|·pëº&µh«ìuõËeYíë.m'=c§´æ´’¶ƒYë&µÞaYÉužw6tƺˉêÌÌš¤eµÏÇoØ*åÝï™ç‘ÿ#3ùlòÔ:Xw2û¡ƒø²YÚàÓë:'c§/gvÝu× Ï@;Í9V&³eì`f¬¬p+­Å_<–'쀌»Ÿ¸6aŸÓ„ÄÎïëK?5ÙG +a˜®`IýðÊÔj¹”á¾ñ¿ –Ö_v&mÚŠu~ó¡6×½e}ygPž†eÓágª|Rø ÀÅT˜–õvØ!™4¶M>=ùË_bymP*·Ÿcá’BE8J£þýûÇÊ¥ýayá:nÒ·)áGbáGéAƒïf÷U6ˆMt ¸åÊ&¸Õb*"®½¬ŸòÔ¡õ Ÿ±3ubeZ+ülEFû¹’÷Ix­l8âˆ#¢Ú‡Û>ª\M*õÓ\щfJ"¼¼p½žzê)WFx>Öéô*\Oë?1Xb@%í>,´<¥Èd¬U””MŒEÙÇpôƒ¼TJXm%“¯o)ËR¸ä:O± ¯°¬{î¹'³é¦›FÏ>ÿµñˆ2Gydê»>ÌÏ:ÏO”sÖš´E9ôCQâ.·ŽIwgäϦp·Öw™N:Å”p('n¼ñF²IJ$€…¿Îá2Ÿý9î—4á‡ee[ç@“&ÜeË›k讀¾ Ö\é9ÆýúÌ3ϤU¥bûøå«GxƒŽ¤ „Ód[çúÑ· …¾• Rß"ÿСC£dÙ _B÷‡Q┕bŸ?6îO‹údk“߿ᆦœ9“ÁX%§O—k)÷ ©ng[*ÜýýÇýzíµ×fp—Ša¤ßà3“ƒB|‘&—TJáÎ9’ßV¸廕wxšðm‡aoK~o¸PÎ'ͪpG’táÊ{÷[>á:ðÍ‹!;++_òÇ9†Ù®g‹ ÚQ õ¥p§“Î(aš´¥ÂݺªÈX÷NÁƒ¢ÿÄüí»ï¾î£K |]£L,Dª¥pçÜý¬ÿ²ð!‰É[¨Há^ÈÕ©4ÕV¸Óiï£=zdrY­£èôŠ8 +Ĥ«£lJi_>è’k¬èòIµ^ü6gŒM©JZu>VŽÑ™À¨T•ñ¤1bD>y3+(<ÏÛB$Ù)ôe À/¦^tä’þ×_}Þ*X×D±zÓáÉöaŸ­0ÎT¨ÔÏ–VûE@²Àº49 êŸ iKžã•R¶†’){Ës)…Kî3™b^”‡%ºu{f¦]¬L“J¼°>\3” iyÓö¥ÅuIS¸3X–Ÿ}XÆJÊ#À·JÒÚÑóÆkr¬ž“ð|›¡ÐNJø½AÿÆ(jøNbFšïrŽlŠÜ¤?^¬“÷Úk/7‹c _¿piÝ+eè?ùw¶uEת?ÔÓf×uèС EH²½…nŸÀºÔÉ$gt³þÁKnоEÑÄ, gÒæõ×_ßYuÓöЊŸÙ¡I†sÎ9Ç]ǰ_*Üù.ĺkó0M¶ë”¬`±ÏúµîЦð|¬‡LXç¾að„¾Rèß…\¸/è÷Ácf?„÷ ÷´¤ñ Ô‚Â=¼ïr­§ýƘ±Áo–çU·nÝ2ÉXY ²?ücМòÇ<çfV¸uAû]P?~ôMøŽc€€ßÛqÇ—:X—ͺÁKÉyÞS·ä»Ãְάó\æÝÁ¬ºlÂï} ¾Ñ“ùCc5ÚÁ34Lƒ¢›A†4=³ø¼gL˜‡õä +ŠÃ4ÌÀ¶nW3ÌXH”ÞI]Ï*f_<Ø ô¢XçÛøŽ;îpeQfò{ߺÎM+>uý9?‹j±ÅËÙoK-@;+E íî(y™öþáŠ"ùƒñ'^¶a§À“H*Ümdè 7*LÝðùYêR†‡Ï‡5@š|òÉ'-¬Æ}žä’N4ù¤š w&a0-êÈ‘Â=ß•©ŸãÕV¸óq•¼¿Q¤ð‘Í˃—ôÃ?œ™:ujßJ®QV¦™‡V7”ç•ÌQ!YVx¡‡õ¡s•Oj]áŽu`Ø&,4Ë#:1~ô:üó!\HÐf‚Z1šYžÅÌHàã-|¨ÐY ëR¨5ašÂ²ø.FPî„ç§ÃDg,ß¿ƒp¦e¤}¼S¥(žV°áo˜ç; ">løÒ· ³î•º•RV[ÉT<•Œ³Û] —\ç+VáEY<ëú`‰‹[1Ü‹uîÜ9vPz¥ÍÂm}ø°>æQv¡PKs½†ò )i wúXß…esß PèER]»v1Æí]r#ïÖ°¯ÁzšÑ®„H›æŽ(4NÀh#)(üµæ~c†Jè–¾MÒH…jRPàørP ó É}êe4ƒê¡b#œaãÓ¡tÂ`¤Ð¿B~†³ž{î9ª‚–(Êp•Èï")Ìô g–b–&á÷fÚ·5y0(óüÒ”iå–òüñå Hóç+„¡ÏÇ’¾¬ÏËàL²¯I?W4á\¹feç*+h)Vç‚M\|ñņ`yˆýq¸àŸÓ³°–™þ»‡ ÖÒÓmÙc4QZû¢ÖY±Ö8Æ~‚N…b-=¢MÛ‘tu°.:¢}¬”ÊZQÄöeÛ°óÖ[o¹`­ÙÒT{¿}ðû 2öámì-çNGp@kqRíS«ü"@Àa«œ5v +j•íà»À”V©ícÅŽªº˜ÙxãcÇ ûQæ‚nú}öCĬºêª~3ç’«Ö•Jd5W0äœÕÐA;r«µ0öãÒØYl?öcØ솀±ïü1–VamìH¿!Ølš§’b:ÆŽÜ;Ê_T±ÖÇ},½µØ(9à Ás­¢/Vž6D@ªGÀ*†bAìâÕØYRÆ*„¢“Òß³V>ƺ ‹öù;ÀçV­E‘ ŸVèž]ÖšÓ'uÏ4«¼sÏAúfÖÊðÚ*ÚܳŽçÝ~ûígì‡U”ǯп³~ŒÝ¦Eá$ûcÉ¥tv™“û³mÓŸ$Ðx(år Ë*wý–[n1v6–+Æ*±Í 'œ`¬•Yìýa?ÊU¾;[ÓØJ<ÛZÇÒXeÆ*&]9ô©­"ÜØŸQõèw’ßÎ5VíÏ·Â5äºZÅm”´oß¾î½_ìû$*@+y ØY††¾G2¨¨ýp7¼‡­‘… ÖI¿ƒmc'V¦UÞ»o$úwiç~ázY+òXPO‚Ï'ÅN¡v@׺–жY¡oc \à>kà;æ7êGßáû‡¾ŽµÜö‡Ý’oI¾xà¨ÝZOºçÃ)§œâ³úÄ”åŸE~_®%Á9“¿ù\é‹=Æw+ßrKµn™_¾•yŽÁ×ú‰wÇ(¾l7ºØYQ DiöÆy¸þ¢5Äsï¾-x×X·ÆÆ8‹ÒkE*MÀιg‹5ôŒõ]Âóðlµn£ Ï;ÈŠÖÃàÊÑÎWèÑ×°†¬Ys8Úø¹w5ßH…ý~?ýë_]ëdt^èJ;Hàúiº¾rxøúÀ6vßï2å.W¬{T×OIûö>í´Óœ¾‘¥u×Zô©ì†ë;’‘wmR>úè#W·°ÍÉ4Ú®"ÖÔõc¥i›ýá;8Ÿ0•†)~a¾0¸ ù“ejœ·\`:G8õ‹'F ±¬MZ„#ÿœ¿ÀIatŒ)-É)la]ý:S# ‘äyí¦lQ¬0ü9Y45)I«¦7†S™V*©_Xqí«åÃ2X3áCÝOW ï¹lëXód›å²0VòÅHhmÅôÚ|Rëî<[B_Ÿü>™ªŸÍb+Dž§!ÃlÓü`ƒÅ)VcaúB×KµpÇ_|)Â4ÊBë–/UÒ”Rå(‘–©þwÉÌAû‘“µ$ÜMø´É)Å¡å-îÆ°âLÜ@…Ö÷ß,YØÇ*Õª39ëÆ×9Û’)ßI©—d¹lkaÎeºu6a&Z8ýüÿøG,)VzžŸ¡Kðß \,ú÷ﮤ„îôÏíÇkT.{…¸K–©í„¿³»îº+g&¾¡üõf:}(XŽÓWHºñðéÃ%³"’‚¥ OÃýMˆàÓ%-Ü©¿?Æ}÷d®¿pÓúC©Äo>,õr,܉ûÀ ål}Bßn–ø5O“J< ÓÊ-öù–QŽ…;å„LCpâ9ÌL·z(<¥Öœ@[Y¸[C³ˆ,®FÐU1›ø¨£ŽÊ<™YbVñ¥É¶BŒföD8¼§s­s¿œ™gt¡Bß /IOáyxŽ23$Œ‘‘­|ÊâÙž6ƒ1,3\§|Üáäê?`±O_1›[±°¼ä:: f ¤¹¤âZà÷+É|ù¶Ñ/Z£Bçk=¿Ÿú[£ƒ‚žÝ¼“ðÖa@"оœ\KúÇIqx´ ¶·p·7p^±® ŒíÈë‹*Jküµ°Rˆ&Ví·g½°É&›Ä,̱¶´èe-Ö ÖB”›õРʾ MÒêÔ>МŦíX:+SÊBìTGÈ¡,*Bk`¿¿­–X]Ð^,#ëú#VߎØNmˆ@@‹Á¸ß&³&°jcd<×±U|8 ";xÕb†Jr»Ø{‘j/ü.ë]hÖK0C>øàgq˜‹&Û¢ÉäO¬D™¡€•bR•Ç:ŠÑÿo¿ý6y¸M¶­ÏàŠ·–«Œ & ZLa5m?x²¶šÙJX6Y&Æ~ôåL—´ºõ‰­‹-gQoc[¸]ÌxJZÞú´m¹¬—RÚÃ;Û×K|ø;k1Ö¸Ä03Õ[­cYl•v.=ýH¬ÚfÁÙr·žöϬá­Û1c•iI¢}Ì0õ‚ UÆúêþ˜–•'`Ýrä,”Yt^Âïúü欢È.jÉ7•·Ð³¾sM®>–ËÙĺ%ŒÑG,æýÏŒfÈx¡=Åô‰ªÙ¥m  žaíÛÐ Kfë0“‰ï’pF }gî)þøçÆ œäìöf`¤6¶>f óWŠl»í¶†¿j }3^Ö…®O˜îë%Y–ß_oKø¼Âº‡ƒv|Ì1Åž—)ÂǤµærëücºŸõÁm‡+(HôBç‰éù\^ðþC‘NƒrLí>ï¼ó|òV_òQo­÷£ó2pÊ”öRÄZØ–’MyD@J$€âÊ ŠØ\³ۻuÉ•®TE`®2‹9V å[5¸ÓŸW^pÓ“¯mƒùä‘R”Å´‡ô¸ á¯A¡Oß#4Ž)&¿ÒG…¹õ¥›5Sø[Å}Œ—ã?>¦l§¯ˆ+Pú¾_Á}‡»š4ᛉ´¸B±³”]¾[Ò$—ë;D¥ õ …:1¥¿­…¾.ß§ÞÈ„zÙ`´n3 T@IDAT÷?Ô>š1xÙlbgÙÉ0à y6¡ÐÂ…¬W£°ãŠÅ|ϼfc¨öŠß]ဪˆTž†‹ô‰óõ‹K=3ƒ‰6R©Ù•¯ÂjKœÒ8ü<Ú@-±#Ù,7c‰òl$-E“J}FºðmŽrÁb…;¾=ñ;B+›X·ÙÕÌ~üØÑï·³f*¦ŠÔ<îfH`u„ïN¬â¼0£ÄNs~ß AƒœEµßÆfRáÎŒÊÁ²Áòßþvs çd |6ÖŠ$Ÿ+ÅÔ År6ñ¹çž)ܓ鸸J³ÜÁRßí^ìlç»õ¤ðË_>åV2_¥·“Jr”.|PÕÚ@f¥Û­òD  ñRŒ…¨Ï“¶,U˜VV)û*¡|«—RÚ¾'ðñ™OBÛaÂõJ]g_bÁP7úá Sýà°O§ee ôïßß)Ü­‹3CØ>öû~Œ™¬[hÿYgeðY›Tjr s)ı% ßjXZ7€Q™á }lö°”ÇOñ2Ë,“-yl­Þ[pŸ>}º«+ý7|‘§µ E‹ÿ~5,ËFÚŒG’&¿•³d¯ÊîbÏM|aÛ`ÜÎ`«Nþ¼po2HÄ7uš‚•rxžòNðZ?{ëx˜òN⽕Í*Ùç爥n® ^<û™5‡4ë×ÕeÅð…÷?AÄ­ÏW_œ–&€‚’ï†Þ½{»%Vçôi0šà·ìÅÆ¨Š¬!™Ý莘=‡KÐPÙÎ5çÛ —B¹„sâ>ÁA–Cã fó É7N6á[‘~ßU|£ñýÃ7W¶‚|—áF ûbÙÊo‹ý¾~œwLIe;m Hc8ã1[=ÃÁ6ú¢áw%³U1vc°£5«O/XŸ§ דo]úfܸ Cpóâ‹/:‹vt<3Â~.3¸®Xîú~s1n†Òê¢}" " "—€íüTUFމcóŠüY‹ƒŒ€e()Ó×m«­¶Jå@pÛY,ë\I ¢ü¹KY&ŠYÅyÞòàg;*±ª|*y~kÝK£ú"PÍ ©NÞ/9!€ªõéžá>´Ó7]a;”±–&ÛŽåI ä a–؇XZÎE =‚¨Z«÷Œí€»`'Çwœ vœ¬K¶@¡¶s±.j2Öú=c]µdì‡cì”ˆÖ ´¤óÙi–NÙæëí—Ö]DIåùúR§PP~ùc¹–(üBAa†,Ì#…{H¨þÖ«©pGáÞ+Å®soåêࢬç#¢ØrIÏ>åI±Vù%—I¹(ã >ެÅ`Iõçc„Èâ¡ ¬O²°nv¢$¤G©ŸLc­œ¢4ÉĹEúdþB·ílž¨xk-_r¹|$Z ´¨¬´>ì ­W¶tt¼ ‘´b>*%" Å agêÅ~»6 ªS~ a€ÏÎbruÖ"6JÇ{ |ž—«ôµ®„’É—UβR\*¡ð²Ö¬|ásÞ ~ ¬bà<©L°nBZ4ßäŽ °£Ü´.2 rð.CÉɽÀ zx.k±îÊâÀ Š ÷ w2k…£ îÒpIùÂß, l‡×ʯ£µÁ)3vfD‹“2øåÓ»´>Ù£òì,ç÷J®òÒîÆ \š9WYvöDTJ­ >¼h.Öòë²"™ÍmÛiyn$Ö§•Â=SÝ쬦ÂÝN1na5ìï›|K>¢“iP­ËŒõßYðó+~Ieµ/ÛºF)ªC”lõNZù²Ó–Ô?iÕŸ,3¹Í9°øK IÉ´Xˇ‚…x˜†gm.ÁzE˜§ÔuÑ=:U¹erßæ”o6Øs‹©Rê‹b¦kOê@»’çà¼ÖÅQ®*ꘈ@ žÍn¸a‹ßTò7ncÉJ%”W®’)¬S¹ëår©¤Â‹ç¯ nYÐ5¢OOˆ/yìtwçµüJ#"'€_gëvÌX™ÁÏpšX‹E÷L'0æ–[nKBkAíöÑ÷² :c‡÷ñuø½ZËy—Ÿ‰¹„8"ø¦ãÞ~8»zöÃØgÏ_ÒZ÷]±ç.Ç*!årÁ3þŒñ¹œOh¾Öy·Â3)IJó.ØbZyV‰çžÓÛl³M2klÛ*ÔŒµh7ÖdÖg´u-â‚§«Åy¸ü\GâÂXe˜óíÎNúg÷Âýƒ¯æ©S§ú]U¿Fщš`%ù;ã]_ªð­Ä½ÀõÃW?ןêÖPªè"é§ð[·³ _nüvX•ØøŠGø³ ­œeó½ffÜŸ|s1lxvð‡o÷°o•³ 8WxoǺrq|ù}[C®¢kGL;³Ïù߇#1oüï²èÂ*”ÿêô¥ù­s]x¾Û™†ÑõÎuTø€'öþû‰ B»ˆYAì5Ió ?Áw«uj쌭Š7œØ"v°ÇÝ«iß—þ;‡¾}úô©øùU ˆ@M˜aŸ¿“½¦¯‘*'Ũ¶Â=Y#>ÒŸ|òI×駃ÌG ®èÜò‡ò¿PE{²l¶é4£(§ÓÍÇA>ùpC!ã?´ÒòÕÂ>”ᢣî(™"ÇàA›ì´ú6øF½P:¡Ä·þv ƒ#°åƒxýõׯéO>æ šå?žùðæÞàÏ@Óž0`[1÷Á¤éDs é(`¶ž>Æ‹i«ÒŠ@k@ñf-ÝûE¨üNùC‰–- a%¾­µ¤d*•‹oK%—(7Qz3°á•’(àxŸ#¼óx·püç}ÍuæÈ@+’Ú"PßY5[hý'CÈ‚ûB‚…V³>*[D ¶T[á^[­UmD@jŒ€S¸ÏVc•RuD@êŒÖ46¸UÕjrš¿z”ÕV[ÍðW«‚r¿zë¶ÇYa¡ª†`Ùj7V£h•)MK€™Cüf«õ»-,ƒsÖma1Yª–¶–¸`éocû¸¿rÌÀ;¨4…}ôQsñÅGE3(.Z" …{-] ÕED@D@D@D@D@Z‰€ éÜ(1£Ë ®ƒ‡ téf½ùc­¹d¦™¡åfJøó2{búôé~ÓÍl+Öõ_”Y+" " " "P%R¸W ¬Š(@-+Ko•rŠ@í˜2eŠ‹…“¬.ÛÜâáZˆ8-m!øùÏr#FŒÐlж¸8:§ˆ€ˆ€ˆ@Nr ˜Š€ˆ€ˆ€ˆ€´6¯$˜_˜E JÀÁƒW%Zk·Sç¶$@I‹´8Mȉô¶é¦%&À!‡bž{î9P¾­ê§óŠ€ˆ€ˆ€ˆ@6齫l©µ_D@D@D@D@ªLÀ+ ÊýóÏ?·8[[+[TH;D  ¼èßÿ½!øx( 'q[ ¿è¢‹Ì¹çžk¾üò˨jÄp!½DD@D@D@j™€îµ|uT7hBµ®lÂK¢&70¬ÈÓ,Ék¡Éø’ïСC-TEu‚ Há^0*%hMµ¬lM:—ˆ€ˆ€ˆ€ˆ€ˆ@ý÷ú¹Vª©ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ ½†/Žª&" " " " " " " " " "P?äR¦~®•j*" " " " " " " "PÃöØcóñÇG5ìÞ½»9öØc£m­ˆ@¹Æg† bÆŒc~þùçÔâî¸ãÅAI%Ó:;¥poÎ:‹ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ƒ=z´™=ëq¨ ²p¯ë Zˆ€ˆ€ˆ€ˆ€ˆ€ˆ@Ãøì³ÏÌ7Þµ§wïÞ¦S§NѶV*C`æÌ™æ¥—^2ï¾û®™6mšiß¾½Yh¡…ÌÒKÿ?{g.Iu­í=È0¸» 2Øà0È@pOp‡àÜ!.îÁ ,HH !l°Ààîî2¸Kê_ï¾ÿª»»ººOé>}æ|ëyúTuÕ¶z«ºNÕ·×^{æ0tèÐ0ÖXíûʰ7räÈpÿý÷‡ï¿ÿ¾× ׌LD ¹FŒ~øá‡X ÷íÇ{,,¼ðÂÍ­T¥w‹Àøãþð‡?„sÏ=7†”Áã]Ö~Ú÷¿oû±R‹D@D@D@D@ºH@â[Á5˜í¯ýkxá…bêi¦™&l½õÖ æT2hÄ߃:(/|Þyç•àžÓ處k¯½6xàQl/+qÒI' 7ÜpCXf™eÊv÷ú¶›nº)¬µÖZ¥íX}õÕ;-¸óæºë® «­¶Z*a›m¶ —\rI{6¸¶J‚{?<é:dh5‰oåÄÿýï‡Ï>û,¬·ÞzaÌ1Ç,OÔÀÖ?ÿùÏáúë¯)Yd î 0SèË«7Úh£\h);B0©^» î=í}¿ï¾û†Ë.»,,±Äá?ÿùOm–Xh¡…ZR* 0 ‚𥨴 îíqÔ ~F¯²UVY%õ¿þõ¯°êª«ö3:ÜÑ™ÞÕxÛ¹Í8㌾ªeØm·Ýr±oT<ÂÇwÜðî»ï†·ß~;¼õÖ[1œÌ¶ÛnÛµ5§Úüè£æ!eÞ|óÍpúé§w¹2BÓ`=ôP ÔÎØcÝå²”QÚÀÿûßÇ{òÉ'o÷¦Æö}ñÅñž5ñÄ·¬½ÔIø•î844£±x¤òÉ'ñÝJÍ8•Y›€÷Úl´GD@D@D@D ‡H|«éâ{XïŽà¾Â +iS(…jÖÚÒz3Ì0C¸ð [_q?¨‘Co¼ñF<Ò)§œ2ƒy‚ &è“GŽ'°{#”wGp÷8ðÄ3æ#Á½O^}²ÑtrýóŸÿŒsüôÓO¥Çpê©§ž…:2ÄX~ÓŒ€{çwòä›l²IXrÉ%Ã1Ç÷FîÛo¿ “M6Y d×Ê+¯8à€0pàÀÏ+­´Rgœq|sé’ûÔgœëxúé§c'  'g]sÍ5Ã[lfši¦ÒüÅt&ÂøÁŒjé~æ{øå/7qLÄ7ç¹ F´s±Å‹Ü–^z阱G‡{î¹'2LË¢}K-µTìÄd> 7ê½õÖ[Ã+¯¼â›òåtÓM™,·ÜraŒ1ÆÈ·³Âù¼÷Þ{c]<ð@Œ‘¿NxvcÎ ¸î¿ÿþ²ÚѾþúëx]<ÿüóqTæüóÏߎÍl«6µLpç‚.^˜Åá}çæ|óÍ—ä„?÷Üsñ;yùðƒ¨÷ƒ}ýõ×±Ý<=K†²Í5×\U7¦´ÍžžÊX÷%žé0îÐ ß*pÄ/.ñ%]¯NÙñ–]wݵãDJ! À#¯cÞq˜tÓß}xB¤á·Ì»“oo Èn'A°@`¦þé§Ÿ>Ì1Çž ”w@ÞG?ÿüóø¾È¤œS5ÑäÉ'ŸŒñš\pÁ0Þxãu”¥Ûûkܨ³«b;bÝË/¿>üðÃX¢ïÒ=ÁÅÛ×W—üqŒ&@üí!C†t™s_e v7F€ûÓŠ+®^|ñźŽ>úè†÷¿ÿýïauÖ©* Qÿ©§žÊ¯ž€kôî»ïŽBH]zé¥QÐöýÅå7ß|_|ñè_ÜÇ÷»îº«ls¾í”SN ¿ÿýïÃ~ûí—oKW]‚Ð~ÄG„/¿ü2Ý׉mxÍçÈ# Ûo¿}8á„:ü}17Ç_fsÎ9gìŒØ{ï½cX)ΉõÁç¿øE´O<ñÄX'sßÔ2&#e?ÆD²„ªªgÇw\ áµÁäɈ‡OçD1N~žÀVøŸÄÿ>üãÃa‡V1ÿIš¶·Ö¹f¸/bœ[®çƒ>¸·šÔ7êµ ±©fÿijáÇgÖËÃß­M•·ÕâÕeö PUž=fö0•§ó›¹=³‡ªªôÞ&{0ÊvÜqǘ܄üÌn@ ·Ù:³_ýêWÙI'”ÙÙ«ÔRz…€õPÇëܼ]z¥~U*" "e6Ì7³‡ìì–[nÉî»ï¾Ì„ªNcùá‡2{y‹eØ‹Bfë.£'2Ø‹@vçwfö’ËËäøÌÓ'{öÙg+¶ûþf-ÍÃ)3¯¤X7\aÝSöÁd·Ýv[f¢\CEZøƒÌâgÿøÇ?2óÌ,}­WПþô§üÙÔ&>¬—´eûLx‹Çcέut©nÿ pðlb/à.‡gz{!ÏÌ /38³÷N—¡ µ Ø‹sfÂ@6í´Ófæu?¿ûÝï2ó–̯K yÿæí—oãý‰w%îEã÷¹îºëf&PæezÙ›nºi1yÕ÷+¯¼26lXžwùå—Ïl~‚ÌDöŠúy´xÝïš™Ím™×afepœ{ì±Gf¡WjaÂSÌKZ?–Ü#¸_ì¼óÎï‘þN9É$“dæYY³¼îì0±-3.²h¢‰ò:Íy¬¢m´Ï:$2BJ«£ó‚ÍÌ«<#¯·Ý—N8aöÛßþ65jTU~ιMršM5ÕTyæ•Y‘Î&ìË,ÄM¾Ÿöð¾Üˆ92o‰]dÉ8¿&„ÅúRÍs‘ž3oǯýëšïìÖy’]pÁÙ,³Ì’·Ã¹˜×k¶öÚkÇëÓ<j›õ]vÙ%žC/›Þ›+¡T›òkÇ—õî5i#M̬ºþ¼ŒF–ܯ¸¿×2sZíVù´aóÍ7/-žgˆyæ™§ÓåÏ=÷Ü™ÍgSZ&¿úê«ÌBfÕ,×Äøø¿¬>{|1®j€6ˆ@‹HpohU#" %ÌS0³!­éÅgî1,lHÆq-3Îø"cñ&«Ê0» ÑŠ—j\«ˆ.moGñ-=ó6ŠBS‘+ÂÏ›©°bš™Å-®xàå%M·Ã;Ä*llhlo„Æ2áº]œŠmá;Ž&t´Ô2B^T R¡1'm›¯#$Ýpà ¥ÅsÎ9Ù ,Pšï‰'ž(ÍSkcWEÉžH%xÕ:C=¿ý¢‹.*½†Ë®ë²müöŠ2guVÍ2-|@‡aajæ/k¢q± ^ Íf›mÖayüÖ®»î:ÏV±¤C¢¬^óìË,´Bé>Ò7CtoTðIÛ[&ºqÿJÓÔZ· UÂôñÇ_•wÑE­`V&dY åŠ4µ¾tEp§µŽ¡Öö²{4בM.ÛpYh²ö%ÐJÁ Ü k]sloTp'…>©ëjÑbçM¸;ÏŠõúóM­3ds@Ô½‡Ëóïtjñ›ÇÉ hÆ)3OÿRˆåt r\e޳”ožîÅ"+¾›|Uç«·«lɳ8ÏÜt"¦ûy¦¢#—gêt{ºNg¤…îÉëï½÷2:Ò4é:çÃ<è«l\›IÓÒaʳ÷b‰é´LŸIK›»"f7Kp·CUÇÎs³¬&ÖîØe/Œé×èº îx‘ã½[/Þ4©Ù°šºé)ˆHdÜ$ð K{ÇëÕSk½’2è ~S—‡{oÐW" ý™€ ±­ë}S|f°¡£U¸x -ó8,æåûúë¯_•¿;ÚQ|ãxl¨tfÃq;|Ž+cdñ;#‹ZšßâFoܲ¼lCO=hmHqi9Åü8qÔðlxrCe¤eZÌÖÒS[ïy˜ÑXwEÉžH%x5rÆz. /úéõÆ;B@Ñ›ïõÔ»šÑ7©!!¸—÷©ôªÁQ¼“¥íar¸Ö,Nof!=+ö#”YQ,Çë“ãEà°ðmã}õöÛo¯*†‘µ<ÁÓ6"<¤bÌž{îYUVw70¢‡ö;Û´~gíKFì¾ûî¥Uâñëy9nÆè(Æi A,}w·°e0êjÙe—Í,&r^FQp?ûì³c™é¹o¦àŽpû´Ý~Í8_ríâ¡oá*Ž‹/Ç{l~Lä§ÍˆƒtÚБZüXت2´¡}´Zp÷#çˆW1ŸbçV£‚»—Uæ¼ÊoçÍôÙ„ôŒÎóß5KîwÚ÷•Ã?¼"/¿a?†âoþZvÈ!‡T”C;8<‹1jÓ¶3zˆ{NÚfÖËîÞϗp¼øâ‹«òzYüÞ¹ÿy‡#£uh÷EêL…l ÏÅoÏË’ŽÅ²{õ“žgÃ4=ÞßõFà1BûËUW]•YüóÜkÞ‡%ÞýÅÿm]éÔk–àεÂ3³ãyàÚk¯MAë•Z#¸S'Þ^ühæ‘^˜<Œíµ×^u? ó<.¸S&ˆ WÃCÈ÷§K<˜ ãÆÂT.têLËå¡…¡c tûìþç*ʶ†måæAÏTñŸ/íàB¼é¦›¼8-E e$¸· µ*œÞÁésÏ„\ÀÝâëV;ž®(ÈâPPôÂÁSшÐuxm{^–x—ö¤µ£øÆñŸõðH"$µ¼Éx΃›¿àÞ„rxME ”'ëœ7¼¡üَΆG»^'=GÔÃy¦lêK_Âl¬Š—:/ƒçS„GÊñêfÝE!_" 1šÑ—evòÉ'GqÒÓ§ÇÓ¨àÞ]Q²'R ^eg¹ùÛø-ùµƒ¨í¡DR1Ä&q‹ït´fŸ}öÉÓÛäzuHÈ%/»Á€<K‚GlP¯§Á+¹h©ÃïƒME…2Þ½Ębš´\DWOëKò#a¼o"¨ê†ûij‡zh¶ÝvÛ5üñδŒt=íüCé¬áIÎ;CÙè€Ô“ž–2KÏQQpOÓ3rVÍÜÓúÒ{pQLÓ•­ÂÆÏ+×W*Ê‘žP¿¤éŠëéožzº"¤âh›>ÓPBtG†yzŒ<÷6¦™ÎÐ4Ÿ¯#Zßia;c„ôü,é„,þþ½<îñiZ:û¸÷÷„ñL—–Mgbg­Y‚»·ƒóÌ[¾öíZVhàîUsÁ¤Ðyçç»j.‰?çyRÁÝ3ð2áû‹K›ÄÁ“U-Ór‰û^fˆîi™<$•fü¨‹C‘žè¬eù´MšA@‚{3¨ªL¨M€8ÓixD%^ÌSC¨å¹'}®( î8ø~Dåâœ4<Ð3 –.Òñâß k'ñÍ&´Ê™ >Ÿz꩞A<ƒCÙ¹±¤c¢žñ²š¦g—DÂýàM!&ñ‰wYÑð˜Â«7uÔð4¯8ðÿ„çEÄ}FÒàü@ûÓ6¤ÿwk•YÜÞlÁ½XŸ¾×$÷±ì„¶µY¯xÞ¾t=ßXgÅ‚=󫓪û»ìÅ7ØD<áž{î‰Kf0Æl¸M°I⠾ݯE%ˆ€ˆ€ˆ€´#s(æE›Æ3‡½tó®hª cO3>+ØÃyÜg^1iÌ[;ÿnþÁ<2ƒ=|ópsÍ5W°øŽÁ†'{Ñ#FŒ{ï½wž¾Y+§vZ0¯ðX¼…/É«9ꨣ‚‰ªUÛMÀÉÓôÄŠ³¢,›ä/Ø(ÅŠby³Q”ÁÄŸ`“`Uìkô‹9^Ρ‰Õy–™gž9˜•OWlm°8ÁDÀpÇw Eì/ÀÇ„¿`qyL¨‰ßÓüí´Î³²›M–8¯Eãx,Vk<8[GC¼í¥®˜4~74X‡D0èŠý[mµU° *ƒ6–¢bŸIÏ7ƒ< áL€ØÓ ëmñíñÇÏsÿý÷Ï׋+ìëŠàÎ90÷`aŠEÖün©Î#/vþÌY3qïh–(ÙÔ1IðrZv‡`næ}…yÿ^¶´¡ùf‹Wœ¯×[±8àõvWí3ÍØ9Pµ£Æ†Î:šÕ(¦j3ïÇ´Ýæ’¨Ú×ß7Љg!<¢ÐžŠjp±‘ áÞ{ï “øXÜûþŽLÇ/9w:Í7tcÅõ¿ÎÃKWÅvêáùÛ¼×sAÝb­å6Þxã¼61n¾ÎÊAÔáÿDv›{$pï•õ?ÿ×]ÓËÇN/²…l‰/=:?Ñ1ÇóFØ•v·v%a†àEx›Ô10ÂÅx!ü%Ü/¼Ïp¶‘‰@J€ŽMD îÇx*#”¬±Æi’| éˆ!œ§FW#T !™ÜŽ8âˆpðÁWýáž‹˜Ô¨µ«ç¦ eס„xoÿË_þÃYCZÀ…ÑTìÇlþÝ«4ý‚€Å@6¡h~¬6™u—CÊ4;$tÞÈ Ï8pðÌ=ýôÓáúë¯ZbÑ»Ýæý©;¢•üÜSçF(*:8q*æùÖCñ|Ëó#!­Ð2e£^Üm’«À§Ìð ïªá ç/<qýðšo•§yñŸ³÷®žIåö'@8›<=6ô¦›njèƒg¼[R#Þ%ÃS c“†uÖY'~< b Þñ oåžP6äIc{ÚÑeIÌ\D„bµó‚SftR4Û!ãϧ61kŒ£O˜Ÿ¢ÙÄ{Á&^,nîð{gÄ¡ k A»‰’¼8iJÒFñ;d~WÄáEh.vÎØ˜aÏ=÷ 6ù^,—X½¼3Ž®†ƒÛW_}á‡Xäi‡-¬lbîpì±Çvˆ€ÎaåŠ(ň«´Nýï¾û®Ã²z2AãšiÂÓ¦F{n¼ñÆ8× #jø_‹1*Þç A$#dL±Óû>#ò]p÷À´|­‹Àè@ +±ß‹"ùçŸHš5Z§œùÀ}Ñçô „vîw6x^%óÉà\ÏÞ~ûí@ìv7î÷Ýw_é¨JœIøØdÍž\ËÑ„@Û„”éIžüóçŸäÀóby!e˜v+lèСÕðc“‰€ˆ€ˆ€ŒžÙæ†g:“¦ÖOxvß}÷…÷Ô‡~øá(ºãÁ^|á!žúꫯð"rÝ_ø‰{éÆˆED|#%aާs³-OŠHWÛñNB¼q1¦‘öÅ¡²†ðý•û/Ç?ÇsÄ0]1‘ý¡Ã“Ž(7DdÒá‰H{uᆃUz,”åâ­§éé%B1ÇM½L¨í´-SM5UXvÙec­§a‰˜ÃhŒß5šx[r¡3^Œâáÿü9¶â±ˆëÌE‚q¨¡Šë‡Q?\)/:‰IÃïÛ ¡žÐ,éõ²Ê*«øîpûí·W“'Rªž¥ÿ7ß|óxNð¥ýŒx'<ÙFmçcÛ[o½‹K'rÆIc^sÍ5ãäät@– SÓë¹(æ×k—ö‰@;üñ+šÇˆ¾ZÆóÂ=÷Üï¥+®¸b>Î'iD îü^ÜQ¡Vylçy—ûaÑÖ›Æ=ŸP[nŒ²+†Ža¤K1ô£§÷å£>ê«qɽ»VCK˜Tžûq»ç³³s’´û15µ}öƒi™Ù(³ƒiècC´j—õ\W”·÷Þ{çùìá¡bßä“OžÙä¸ßþ‰æûlöú2ŠŸ—˜>›ðÜaÃZ3ó²É?üðXå™0‘ç7ƒÌD‚<wW>þøã̉ÌDà¼h²³Î:+mbR¾Ý^b»ØabF¾ÝČ̟Åìe+ãˆm‹òœËuælYZ§LžÖ¾s@{yFHí£>ÊLdËóPǰaÃ2=c;l´EÆószL&¼¦Edö‚˜YçCž†6˜—Ù0ì<ÉLœÊÓpíšøVÁÙF§æû©ž&Æe{íµWf¢lÆõg"c'oÅFÍëÐJç ØHŒœ¥3eÉuË}-ýͳóf/Û¿yÏga2 JËó4eKΧ æ±ñÖ¡T3ÿüóÏŸ ¿“²²L`ÍÓ°ÂõnÂIiÚb~~Çsj—_~yCyÓ²zòžœ¶…uëÜíT{¸—í‚ .èTé±qŽS3g¶xM¤i:Z·ŽÃ¼þWt”¾¸ß¯Á¼ÂЉc™k¨\þ'øÿ`îùźê}çºr]¡Ð}mõ žSþW6ÓlÔDf#h2®mþ¯¥ëüª¸®L”®ØÏoÔ:3묬h¢ÅþŽåñ¿4½­C0³N±Ì:I+Ò[˜µx_fšÞïŒßiñÞV‘Ù¾ð»IóY<öÌæuÈFŒ‘YX®Œÿáæ’Ù(Êÿ÷äá¹ÉÂÆ"yÖ1öв,¼WfqÙÕW_™PŸ—g£3ë¤ËL´ÏÌ»»"u°›˜¡ÍuÔQñYÆ6×KEžeØž~¬#6þ_2çÛªòêmà|¦<Òuž§¬±^ö¸Ï:ó*Ê€ Ïñüß…·MÐßxîNŸÝÒºxÆ5‡‡ŒÿEé9„7÷ržÑÒãeö¥epŸKÓÀî´¡³fy[§›nºÌŒ:[DJÿµû…´ÐŠ‚;/’œp>Å›IOî\ˆ¼ˆ¤ –¾ ñ2Rf]Ü-ÆjE<ÊD U$¸·Š´êÿ#`!b²%—\²âÿúüQ¶Ž€›šyöu*£ÏJiõÖÛM|ó¶ò"iC•fS&¸ÓqPvjmóçEoKó’ÍÌ#·Såxùõ„aêòtõ–¼¬ùK¨y&7”§XžM˜˜R\ïŽ(Ù“©¯ªSÓô ü¶Ìk7£ãɯ*Ÿw|¥‚;ïKttaiç;æý·›×[ì°I;E¼Ü²%b*n i§ùÌk2³ ,=YfæVˆ-ˆ°æŸ™gržÆWp„:à€¢(TÖ†¥–ZªJ¼ò¼ªbƒ Êù”•Á68ð;OE/§§–ˆ.æUÞa[h˜6ò§´j8Ä’²cá^é>Ä7?ÇÅ}Ên:áu\HâQ¶rÈ‹0øØÚèõ‚°ç×`^HÉŠyÆgtФÇàëtäÑqg£á3›#Ïm£¥2ÄIþ MECÏO§9€ÞI” •¶#Ð*Ákɯ®.ѦÒ{Ç´ÓN[·L~W<¸Ñ_¯î[o½Õ“–.¹þ‹b}½òÒ}¼<»Ù(”ºmIóÖZçÜ džZé;ÚÎïÞæÖ)Yó;÷îeåÚ¨®šùŠ;:û\_VŸoKÿ¿]|ñÅ¥mó´,ù¿êŽÅv—}¿ûêäÿŒ¬&ÞÜÏ;ï¼¼uô:¥† eÌ÷±B:E«çáNÚ{ï½·ªçÁf îÅŽ›Ý¼Øl}¦àÞ4´*XD@ê° ú2›|/C°JŸiÒu^â-6{©0„G0žã¼ì×+é2ìºk`g»‰oi“-dAË„/¼šN=õÔœy™àŽ—~Ñ“;=/¾îÂ]­—S ˜Y8‚¼.ÏǯwD)<ßîå¥/Jéq±þõ×_G¬Tôôü,¹&™øÿžžåˆ iÚZë§9³™…ŠH‹È×»*Jö¤@*Á+?Zi ½’ѱxÚi§e>)ÃIª¿÷;î¸#vdœqÆÙßþö·Œ´®Nn¼sÛ\™…ÔÊ,^µœo:+QÄÿ’FŒ_‡>z‘òÿ…ëÅBD4äÙÚHùJÓ|­Üñº®õ¿·Ñítäà)!‚ò¿^^ž¡æÆh°zéédëÈ•˜Žp¬Wžï£c€ÐÔh?Ï\éˆFOßÈ’g©|0-2®wÖq¢XWñªª‚ÂôÈbŒøãY¿Q³Ð]YwÛímHÏaÚéíû;»¤c»O}?Vê/ÖÁ9öëÖÓi™h/ÁJÂË(ÿÐܸ‰ñÂb±¥2‹ûæ›ã²#ÁD L/›¥â&ÖÓî¼¥õ=Ø*@_D ‡ Hpïa *ND@:Iñç›|.CÈàeŸ—~¼Cõ°A,`H1ù(*¼ö,®u'[3z%G4ã¥Ðb¹Ç!¶È(¼Ôø³^;Í6„)¼GeÀKB¢uwŒ‹“ž{î¹N) ½N=غSv£yÛI””àÕèYS:"V î<×ÑÙÞ‘Wº?£¤K<ÕñÜ&¤Zjt1£ªŽÑ „):§ò,´ÖZkåzš×ÁH“wܱaQ”0#„ŸóüeKÄþÕV[-v^ÖŸá²ÕV[U9¿Ö*g“N8¡f¨&:Àqäu !2Ú=±³†\ Tö°£ry–±yòÑ>Þ¶tió„Äp=„ìK·³^v)““Åöó–}GkÅ1„çèΣŠNŒ‚“Õ$÷ì¶Ñ³apÁ†ÚåuÙËD°ø›ùw ƒÅ2 Ë,³L°‹ ß΄:>Ãï,žT¾ÉÒÉQ,\8å”Sòý¬0‘“¬0™U™YOU°‹¶j—yÕWÌlCuâ ÷U “ LÊC{Ìc"ßj7®8)M¾A+"ÐDüVøÍذË`YM¬IE‹€ˆ€ˆ@{°‰`/•±1LŽØ™‰KÛãÔ ž"À”Lm1܃͋ÒSÅö‹r˜ÀÔb·ÇÉËÑÐÌ16˜¸u. ñ¬# aL˜lN àù˜H'5/þ8!5“R[gE°‘x —Ù2<“ZØêðî»ïÆc6Gà8A3“p[ç@Û‰ÛhÜ8Á­Ö“x÷…v÷ØoŒÍøcõRå¥Õ"òIYÏ]lg;âug„ k \Í4óB ̬žŠíOPb{3¡«l~K€çD‹ ,”OÎÀ¼Åòu­ˆ€ˆ€ˆ€ˆ€4N`Þyç |zÂ,LMXh¡…â§'Êë«e ®óéËf!d‚MüÚ—¡åmo+Á½ìè‹ø6ĺ,Y‡ÛÖ^{í°Á‹¿ÖaÚÎ& K5X¬öØÛãùm¸M°Ùœý«–" " " " ]$€WMÐÞ|óͼ<¯lŽŸü;Së­·^þ]+" " " " " "Ðj-Ü-†i°˜KÁbQVÞH„™©gõvò^yå•Ù-žf ÌÌ\sÍ,þTXsÍ5óý6‘K°ÉYC!:²«®ºªj8²M–[n¹<+mã%!!;4ßî+»=Ì7ß|þUK.°9yê†JcXòwÞlÂÙ.Ö l" " " " " "Ð}MÜmb«°îºë–¶ôÅ_ |ºb6qXE6®±Æa÷ÝwC† ©™†”…ÀŽýZk­Î?ÿü8³b‡ÖŦ" " " "Ð% ŒXôÏDMÔ¥r”ID@D@D@D@D@šE é!e¥O=õÔøéÉƒà… o¦®âÿ·ß~[3ëcŒÎ8㌚ûµCD@D@D@D@D@D@D@D@D@D@Ššîá^¬PßE@D@D@D@D@D@D@D@D@D@D`t$ Á}t<«:&–àÞräªPD@D@D@D@D@D@D@D@D@D`t$ Á}t<«:&–hú¤©-?"U(" " " " " "0Z¸øâ‹Ãûï¿iÈ!aýõ׭ޝ«óã?†Gy$¼úê«‘Ï$“L¦˜bŠ0óÌ3‡¡C‡†±ÆjßWþ,ËÂÈ‘#Ãý÷ß¾ÿþû ³Í6[Øpà +¶é‹ˆ€ˆ€ôíûß·¯T;E@D@D@D@D@D@šJଳΠ?üp¬c­µÖ’àn$®½öÚpàF±½ þ¤“Nn¸á†°Ì2Ë”íîõm7ÝtSà\–ÙꫯÞiÁý£> ×]w]XmµÕÂàÁƒËŠÕ6h …”i fU"" " " " " " =C±z£6ª)¶S˧Ÿ~®¹æšž©° ¥ô´÷ý¾ûîvÞyç°é¦›6¡µ*RD@D@' ÷ÆY)¥ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@/XsÍ5Ã|óÍk^h¡…z¡íUån»íÉ‚-¼ðÂðqÇ7¼ûî»áí·ßo½õV '³í¶Û¶WÓÖÐæG}4)óæ›o†ÓO?=IѹUBÓ`=ôP ÔÎØcݹ”ZD@D@zˆ€÷©bD@D@D@D@D@D@šCàÐCmNÁ}°TbÙ¿ñƱåSNÝ€L·@IDAT9e1bD˜`‚ úà‘„@ç‰w  ”wGp÷8ð?ÿüsà#Á½O^j´ˆ€Œ$¸§Q!" " " " " Ý'ðÅ_D¯c&ÞdÒÍÄB_ýõðÒK/…f˜!Ì5×\ùöZ5~ûí·áå—_~øaLÂ$ž3Í4S8p`­,MÝN|ïW^y%|þùç±LÊÙmù駟“O>¾ûî»°à‚ †ñƯ©ÇAá>y,ëÔÙU±½ÝÎÇÓ.öñÇÇp=Ÿ|òI˜zê©õv•s»“Ú!" "Ð:ŠáÞ:ÖªID@D@D@D@D@Ú‚À¨Q£â¤”ÓM7]˜l²Éâçàƒ³Ì2KX~ùåcø&Ÿ¼ë®»ÂÒK/·¯²Ê*ažyæ K.¹d@€/Ú;ï¼'ñ$ÄÉDMæŸþ°ÒJ+Å‚%"þA1kÙ7ß|6ß|ó(ì{»j-/¸à‚ZÅäÛÿú׿ÆãA4¥ÝÇ0í´Ó†=÷Ü3†_ÉVð‡Åä“Ož3¢-¯¾újìHØe—]ÂÄOYd‘°ÔRK…é§Ÿ>œyæ™…Rzæ+l×]wÝ0Í4Ó„e—]6/ôÎ;ï¬hí›sÎ9Ã!‡’§IWºsŽ8çk¯½v ýœ¬¸âŠiñáÒK/<|?Ëgœ±"MO~áür^©‡p:nœß´ ÞŽ-¶Ø"¸'¼§õ%^ñ^xa˜uÖY#_|ñª‡ë™ï묳NXl±Å#Þi)" "P›€Å}“‰€ŒFžyæ‚9föP8•ED@D@D@D ' \tÑEñ™‘çÆ®|L„ÌL¸¬h’‰± •e¢efáyýËßÿþ÷†Ê Ígu–g«Z~õÕWÙf›mÖaY“N:ivÝu×UågÃlPšÿè£ÎL„/ÝG»þð‡?”–×gœqFÍúj?óÒ®ª²;çèøã¯jâ‹.ZQ‡‰ÕUiÆüŠ4µ¾Œ92ÏkñÝk%«ØÎµTëøkm¿å–[*Êà ×ò2Ë,ÓpYO=õTUÚÐ>¬3,žËÃ;¬}¥–ˆ€ô_ÛÿŸ w(ÈD@D@D@D@D@D ÀÓ{…V¨Šs5^ÌLÀ™^ÞxX㹎1Ùåƒ>˜&‰áM|ØcŽfŸ}öðË_þ2àUŒ§8Û0Vƒ‰Òž´bI:òL8á„aŒ1ƨúxˆ›ŠL%_¶ÞzëpÅWä{ðpæx7Úh£èNÙØ§Ÿ~·ÝqÇyZ_Ù~ûíc|ñ±ÆªŒÄŠ÷8#ÜðäŸ{î¹ýk ¥“é¡•µÖZ+¶Ÿóâm÷¢‹œðæß}÷Ý£g¾§ñ%!hÜ:{ŽÖ[o½è]?Î8ãxUËýöÛ/ž÷b«öІ=öØ#²÷kË‹-2á;×.ñ‹_x²|yòÉ'‡»ï¾;ÿnqd‡uÚ„áÇWýN^{íµ<­VD@D@D H òÉ¡¸WßE@D@D@D@D@D@F;ë·ß~{0/î`ÞñøæwÞ8'âô–[n.»ì²¸°3ˆ‘Ä=ßwß}Ã)§œ·»</?üð(–#V"¶c¤#²#b÷ß\ÿ'û¦›n*nοSÇG‘/[¹ä’Kµ×^w!´î¿ÿþÁ<]+:h;âû /¼'ØDXEDM;V]uÕÀ#dL±Í„n9÷Üs£ýßÿþ7\sÍ5‰?S£nB¹4jÛm·] “’¦'>ç {üñÇó‰FW^yåpë­·¦Ië®wçˆCLØ:xðàÒzvÞyçÀƒ%±í›i„⃺æí·ßŽë_ýu4hP\oäÏ=÷Ü“'#,Ðo~󛼃ˆœ?®Ý믿>¦«uüy!Zè×$¸÷ëÓ¯ƒÿ%pÚi§Å¸×|#fµÛQGÅöâö²XØÃ† ?üðC°0‡1Ö9"6âüÄpw{óÍ7}µÇ—§žzj^æï~÷»@û‹†W:â8Kb“ðÁáꫯxÆ7b›nºi8ï¼óò‰4ö7Þxãø)æ'θ… )n®ùÝB›T î5waG;œ£.4»©Yˆùîvúé§æàÚ Þ?B>ñùÿô§?Ź ˆãNç”LD@D@jà^‹Œ¶‹€ˆ€ˆ€ˆ€ˆ€ˆ@?"0öØc—m1¤Ji"ÛȤ“x—ãÞLA½Výlï½÷ÂO<“ ðãÑ]Ë[sä‘GFo~ÒÜ|óÍ î'tRôô¯Un;oo‡sÔŽ|vÚi§8¢ãÇ /½ôR8à€òfr,°À1¤8Œ‘‰€ˆ€ˆ@=ÜëÑÑ> äûŸÿüg‡i›™àÕW_Í‹· =+‚ä;’•%–X"ÿÖh\n޳3öÀBÎ4j …Òh™¤k—sÔ™6·*-^ë6aoÚ‹£¾üòËpï½÷ÆÏùçŸø0Ï€LD@D@jà^‹Œ¶‹€ˆ€ˆ€ˆ€ˆ€ˆ€4D€¸å©ØN¼w&f%Ç_|c`#†§±²*¸“‰&™d’<ǻロ¯×ZI=ñ'žxâZɺµ}¼ñÆëVþžÊÜ.ç¨§Ž§§Ëáz%f?“ú'Ÿ¡‘ðxÿý÷cuŒ øÕ¯~ãõ3ÿLD@D@ÊHp/£¢m" " " " " "   ñ­Ý)sðÁWy—#n3ùg3ÉZ ‚W2žÊt¬±Æ¥UâuNx·\ÐWG»e³Î^óíhY–5Ü,®b÷Ó!ô—¿ü%,¾øâñ“ÀµËä»ìÇN>ùä8ápšFë" " "àÆð-E@D@D@D@D@D@D ³ðþꫯb6â[çzÌ1ÇÌ‹AüdâÐõ×_?ßÖ¬•qÆ'l»í¶±xêÝl³Íµ×^[UÝ'Ÿ|¶Új«ð¯ý+îcÒSâx®Ö“爑 ˆ¨ž~úéðÙgŸU`£“Ñú»ï¾«ØÞì/éH‚Gy¤ª:ÚsÍ5ׄM6Ù$œrÊ)ùþÇ{,Æo¿ï¾û¢ðþÆoäû|e¦™f Ûm·Í=Þó Z„€<ÜZþ@`Ô¨QQ`f¢P·í·ß>ìµ×^a—]vñMq‰·:ñÍ;ì°Ší'žxbxûí·Ã 'œ&˜`‚(º#d3ÁärË-˜´”?üp(Š˜Ll:Çs„5×\3z WÜÍ/‡rHŒÇM›?ÿüó°á††aÆE˜6½ð áÖ[o 0p;è ƒÞñnˆÈp Bm**#&§ “N:i 3’Џ^NO-o¼ñÆïçŸ>üôÓOy±wÝuW˜rÊ)óïásÏ=wìô <ŠÛ!Czì¶gÎ9çŒmÁs~Ùe—1Í?üðÃ@¼zÎyjß|óMLsì±Ç†¥–Z*îB¨ÿío¯ ÷’OëöÛo¯8.Ž¡üÐCM‹®X_d‘E‹/¾·m¾ù汃‡|üqxýõ×Ãm·ÝÑàꫯŽ×!<ÛÿýïÇër•UV‰×ù¹NåGŒA’hK/½´¯j)" " Õ¬×_&"0°\ÆOföp8•ED@D@D@D ' \tÑEñ™‘çÆôcÞË™…ØÈöÙgŸŠí&äfŸ~úivÜqÇUl'ïË/¿œ]pÁUÛÓrë­“¿Q3Ñ?¯ç¬³Îª™gb‹#Ÿ§­WÿÎ;ï9-ìòË/o(oZ® ·i=º>|øðNµg¥•Vªª¿'ÏÑ%—\’qM¤ÇßѺ æy›öÞ{ïNå¥l¿óB +s=wÜq*×&IÍLè%Üÿý åñã㺲΅BíúÚN¬³,žSî2h1¯íÿEPH(ÈD@D@D@D@D@D `‚ÈVX!‚Åmúé§qªñ’N ¯ðÝvÛ-¤’²Ÿï[l±E`‚T¸˜¦›nº4k¾Ž×ûgÏpÏ_±£¾Ì3Ï</zêÄK¹Ìð¶ÆÃÿì³ÏÎC¤xº•W^9˜h ä›j.áðë_ÿºÂC¾fâ.îØu×]£Wy#Ù9ö²ð8=y޶ÜrË·ßîª& <8zØ=öØq×ç›m¶ÉÓnºé¦ÁDï@(ŸFŒsXv ¦y©ã¦›n óÏ?º9_8p`X~ùåÃùçŸî½÷Þ¼nÚ±úê«ÂƬ¸âŠa¬±Ê.é˜cމÞîµ®©¼2­ˆ€ˆ€ôkPùû5¼Œfž}öÙ0tèÐøbÁ°N™ˆ€ˆ€ˆ€ˆ€´ŠáC|ðÁ¦ã‡~ˆ“¤Î5×\1LGO´0$„°ÁÎ;ï¼°Ã;tX,aOÌ‹9†Å!ÄÌ 3ÌZh¡0묳v˜wtLГçˆsüÐCEzê©§„®Aø.vÚ´’#aŒ)ôÞ{ïÅk0;t ÕÓÓöBfäÈ‘1ï_|;õÂu\ÖÁæÕz{ ƒŠŽ4Â`~øáíÑ(µBD ¿øÆþŽ/Á½¿œng¿! Á½ßœj¨ˆ€ˆ€ˆ€ô+©Ä}·4ñ¸U‰Û-”€÷”†ÖE@ZL îåc¥ZÜU'" " " " " " µ¼ùæ›áÜsÏÍÅö 'œ0,¸à‚µ’k»ˆ€ˆ€ˆ€ô ^‹€ˆ€ˆ€ˆ€ˆ€ˆ€¤áq衇†ûî»/|÷Ýwq×O?ý^xá…FCŃuÌ1ÇL³j]D@D@D@Ú‚€÷¶8 j„ˆ€ˆ€ˆ€ˆ€ˆ€ˆÀõ×_N?ýôº öÙgŸpÜqÇÕM£" " " "Ð[›¼·Z§zE@D@D@D@D@D@ú e–Y&0Áeј쒉N/ºè¢pòÉ'w뻈€ˆ€ˆ€´ y¸·Í©PCD@D@D@D@D@D ˜uÖYóÏ>>ýôÓðý÷ßG “O>y@t—‰€ˆ€ˆ€ˆ@»ÐK»Ÿ!µOD@D@D@D@D@úI'´Ÿ±WD@D@D`t! 2£Ë™Ôqˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ô* _•‹€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€Œ.$¸.gRÇ!" " " " " " " " " "ЫýWñ«r¾HàÅ_ ?þx—›¾á††o¾ù&üãÿˆe¬¶Úja¢‰&êryí”ñ§Ÿ~ ×_}lÒòË/¦œrʆš÷Úk¯…‡z¨¡´žhÌ1Ç ë¯¿¾ÕRz€÷^?j€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@_#pÓM7…}÷Ý·ËÍ^o½õ‡~6ÞxãXÆÓO?†ÚåòÚ)# ~\#FŒhXp¿ãŽ;ÂöÛoß©CwÜqû¬à>jÔ¨ðú믇±Æ+,°À:n%n_ÜÛ÷ܨe" " " " " " " " mJ`Š)¦¨)?óÌ3±Õ“M6Y˜vÚiÛôÚ»Y³Í6[4hP‡l$M‡…ôR:m¶ÞzëÀu‚ø.=Hp=ΣŽBD@D@D@D@D@D@D@D …¶ÜrËÀ§Ì 7o¾ùæáŒ3Î(K·M:é¤á˜cމëSM5UÍtýqÇu×]'¯ïþxâGƒc–à>œD‚ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ß#0É$“„ßýîw}¯áj±ˆ@MÜk¢Ñhï¾ûn \ÍüóϦžzꪆ}õÕWaäÈ‘aœqÆ ³Ï>{išªL¶áûï¿O<ñDȲ,,²È"1¦xYºâ6Ò¿üòË d§™fš°ÐB …1Æ£˜¬Ï}ï*?Ð>ø òœqÆãy{ì±}W—–ÿýï#ã7Þx#0I,ç~Î9ç ìRyÊÔ\-Üxàpã7†W_}5¼ýöÛxW³Ì2Kü13™“Œ.Æ Ž‰f˜a†0|øð9¬®–IL(&¬èŒqóžk®¹:“EiE@D@D@D@D@D@D@D@$ðÚk¯…Yg5¦N'M½í¶ÛÂÊ+¯ã¿#²_xá…áÈ# ®nsÌ1G¸ùæ›c~ïm¶Ù&Ší?ýôSL‚ð½þúë‡3Ï<3Ô WóÜsÏ…ßüæ7á?ÿùOøñÇc¾ &˜ ,µÔRÑó~™e–ñê*–ŸþyØm·ÝzÓgŸ}–ï›rÊ)ÃÁ¶Új«|[«VN?ýô°×^{Åêà:xðàÒªÿøÇ?†]vÙ%L4ÑDáý÷ßLºêÖYÅót饗†£>:¼ôÒK^dìü`ìz¨¡e—]6Ü}÷ÝyšO>ù$߷ꪫ†ýë_ù>Bzê©QcÌ7Ú š*眲%¼§dz½eêöã?öØcpÏ=÷Ô<êÿùŸÿ Çw\>‹qÍ„}dÇYgø¡m°Á=&¸wµÌwÞ9vptÝ 'œÐgwþIÑIOb­*a¡´" " " " " " " "ÐN:餰ÿþûGQu‰%–_|ñE@Fd_{íµÃ¹çžÖ[o½ðñLJ!C†Dïgöøá‡ášk® ˆ¹ÿþ÷¿sA×á²Ë.‹b;šˆÎx§÷ÝwáÙgŸ ·ÜrKÔðpš]a…ø` ƒBDеÖZ+îC³{þùçÓ¬aë­·Žb;akÐ]ðpÇÁO|Äz4'<òÝ;ÓC8oäãåã]¿úê«Çªþò—¿”VI§Äˆ#â¾-¶Ø"OÓB¹xõ£k²Ž ιÁyذa±žSN9%>g<üùçŸ÷1y.ßùÐÉÑQB Ï}ÂÔÜyçóKdžÇþçX~øá˜NÚƒ@K<Üé1úè£bOÌwÜãLŸ8Q •!ç /¼ø¡ôeóxJí–eÅW ýë_û2Vµ]D@D@D@D@D@D@D@úÄä6Ú(V$µu×]7,¾øâQ$Ç£g¼ªÝpx=öØccä¶!ÓÝ(?üðCŒÊ@șԈŽ Où>úh  áZ0Ä`Âcx„£ù¥6÷Üs‡ûî»/ŠîˆþÝ1ª;*ãÞ{ï¤CôþûßÿhÂY{¸/ã†n?ÿüs˜yæ™C.§«<¼\:~ñ‹_D&¾å ,ÅòÍ7ß<|úé§1Þ=!œ=¦»/ 5CFjt€»Ûa‡bìvßO™cŽ9&\yå•f„øFyyZ6@Ó=ÜÐéÍÁˆáÄEUf\€ ½À¸½·©,m_Ù†—5½M 둉€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@g Ô B w GÏTl÷ò§Ÿ~ú¡W0ÂÊÍÃÉüú׿ÎC°t‡GZþ˜~Í×é¡Scô@£F˜ùNØço¿ý¶*+^ôŒ0`„¬}4ÝÃ^-7f9®g+­´RôjG¤gˆ âõŒž8z蘙²‰Õ¨ug¶a~\Ðü€™ðµ;¡Wø±ø²ôZQ&Å^­F«ÝÓÁŽX_ÄœböêΓ~0< fóÌ3OCÜ}XQ­zžÃ°!†ûpgši¦0ÝtÓÕJ®í" " " " " " " "ÐRx—™Oö‰·v-4hP•PKH Qutš2CŸÃpŒE#t¯Çm_mµÕ“²Ö2„ï8 Öã©^ëØÓf˜a†ü+žß›nºiœ(–P+©ÎqyÛrË-ó<Ýá‘b+eìç<Á Ïú2Ñ<-#]ŸvÚiÃòË/ÛÌ‘Äá'L7Ñ+†õ«ñÆ/Í¢õ6!ÐtÁDn÷ßÅp ßîKzm²Âš‹*5zƒö±í¶Û&Ød“MbÌ"†l`\¸üNÁÅW˸‰teöeŸà€+1“Rã&s衇†%—\2݇±ø¤©Å^5^ÚJL+&»HY’Cœ&n~½ið¥G”sBŒ®‹.º¨´9tŽØb—Ç‘"1ûˆÙÏ%ö»Ñ»Æk„ã?>Jãû–^zé8‰‰?žnŠ_ýµ' ‹-¶XøóŸÿ'a#ÃgŠyУ꽪tâ0¹†ÈNÿö·¿Ÿ½;î°?Ì}ÔQGÅ¡@¾MKè õ„mÚÓÑþb›‰³Ž¡‡Ö¹cVŒ‰Q±gœ1.ký©× P+Oq;N‘iœâþZßÑР‡Ã¼’^:z’ ŒîðHÛ0ÕTS¥_{d>ûî»oœ—¸íçœsNüP8ºQ5ˆïÞ×Ãr÷¬6*¤éj.1½Kx£Ÿxâ‰^'†mÔ2âÕ3Ûå–[.z¶S.åã¥Ìˆ)„yÄïÃ?¼ª˜®Î6üæ›oÆ ø‘G‰eÒ3…:u"œ3É+P°, ¾U° xä#Ò{yÇ /9áÁÿ‡?ü!ŠË\pAY-ÛF,)n¾Äê¢Ãœö‘ œ:MÒóˤæˆá7K†ÑñÁ¤|àH‡DY¹L²êª«†‡z(pãbÆl!¼3 XUtR4jÔÇ5ÄŠör.)›ö3b‚pFL²ÀÏõ%Ñ…€ ôh>8B6bîIí¡Q|Y+¯‡B©µ¿™ÛñÄ'2B:zÓA«ó‰fSïvvt‡G3ƒ²áN ýÃ;,üóŸÿ ·Þzk f=ññÑÔˆÓ†È$¬Ò°š}6:Q¾…ÜhºtÒILGœ¬g)3¯ãÌzi2óœn¨þuÖY'ÏOYæEÙ0Œ<¯]h™Mv§ùÇ?þ‘ïcÅ&aÍLÐûmöåì›o¾É÷›ÐšÙìËqŸ‰é™‰Áù>VLðûÈÉ%—dÖy÷[O`v×]weC† ‰û-FUf1±ò¼öŽÛ-4N¾•C9$nŸp 3Úiq°òý“õNÅý"'£ŽÔj•™¦)[·ŽŽX&ÇIû;úX_^ŒõƼp¿é¦›òíéŠM.Ó˜nάW1n·žÍìî»ï®ØwÅWd“L2IÜ¿Í6ÛTì³¶ãvú”Ùd™ÝTòý0¶ÉBâ~ÚdÞóqŸyÒG–ð¤>ö|òÉù6öcÖó÷Q®Ý|ã6ÿc‚{fq¿ýÓñÍ}jiO±ýÖ»Ù§Ú­ÆŠ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@w ì²Ë.ñØÊîÕ­üh|,¶vÝr,ÌpLGZ Û’§µð!ùöTÃÊØÊŽ;îÓXø–tsźEžˆiгÜÌa4n3¡9׸|_GKsy©»ž™Óq\hgÚŸþô§<ŸÍ Ùh¶ªtó<–cqÐã>sèÌ,|sf ™9ÏV¤ïFΕQ/,,†|EÝ?nŸl²É*¶wô=‹kݵNspí(‹ö·†@ ÍQ;Ø’]=e } 7†YŽ1<Œñ˜fRQ†<0‚°Ä"²cï°Z¼× B*7ân“h¬8éCq¶asEZŸ}/sb)ÑV·{î¹'ŸH‚‰!裣7ð#„¸ÁðÆ¦§©#óxQLh@Ob¯còI)ð„§·ª'øWx’wôÁÃÛ ¦>Ù-!uŠÆ9»þúëãæ´—.„Ô!,ÞñÅÑ ÄÔ"¤Fhž7üÿ?LÊaÂ|ô8÷íÄê‡ÿñÙ±é=…%ïIå\·Úc6êb,0âmùèÎ}1ÜŒ·AKè‹ðþÆüä“OÖ<„wß}7œzê©ñã!~çœsΘý«žfB(”Þ4¢/ Û™hà ÓÂE£Ãù¤ªÞ¾îðð2š±´ŽŠpöÙgGÏöbùèYguVØyçã.¢sBÖZ"¸s¨ÄMã7¢Küõ4¶yŸóX«¬²J ­Bh–Z†ØJl¢2CDöØáÄi" ÖÙ†‰„™ÇtØxããzñ1㉷~ÄGóZ/î®úNçóöÛo_µ iLwŸº4a 7š§z¬xW„JíÁŒ³,Ó‰‘Ntk½’±…ŽfV.3B1*1´èÐ(Àj…éñÙ°;3Ã3åÓ±‚q=ðÀq=ýÃM™ž¹n:"•æÓºˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€´;⯯´ÒJ±™„*©e{ï½wØgŸ}ÂyçÆü˜ }{ýõ×kÎó‡C*aNzÓpÌõc$¬Œy–Çæ¤Ž¢Þ¾îðð2º»,s@~â‰'‚Eé6š ¦ƒ2!“ÝÐLeíA`¬V6Oã-¶Ø"~¸˜¸Á“8Cô4ñƒ¤ç‰™Œñ˜¶ð'UÍÃKÚB·Tm÷ iì)„üaÃ†ÅØîìïÊìË´ÃãÛc:Å …?Ü„µ6Ú¨*)¢11èáAïU³Œ›MêÁ_«žti6ß|ó8³3çè–[n‰£<¯ß´,ìOE‡ƒÏòŒ—ùÕW_íÉ«–6l&n³3U3Xךᙠ~#éÌ Ïä£Ã‡š&ÎêÌŒÏ\7LÔŠO{ë]c”!¾JÀB®œ-Ôqœ{‡S÷üþôÓOÃUW]<ÊÁ;ì&Ñp˜ÅÃïj(ÙïÑ ˆj3&×Ý5&hõr;*‹èEÝçQ4,¢/àTiáu¢æXVVWy”•Õ™m´ CoÃqxèСñ˜9nw@%^ûÖ[oG¸†Fæ"d¾LŒ|©ssܨ?½F ¥‚{z”„ü` ~ü@ñ' 눢\P~áy^<Íë“ L3Í4ñ¢{饗bÒîÌ6ì³/3ÙkOšÅ®¢/áeøA!þºY\s_íñ%"µÏÎܙ »Ãä¥Üˆ¹áâµîæ“NЙ’šs'½ß¤ÓýÅõ²2͘á™0@LrËÍŠQœóC-<ÿ4ÒΛb[õ]D@D@D@D@D@D@D@ú* ³ÿþûGïo´ô"t¸Gy$üüóÏñÐ6ÜpÃPt2%¢ H‡ö~ûí# ã¹ÐÎD¥6ç^·ðxdƒF !,³{á{zC'šh¢èðË6ôG¢c”Ywx”•×è6M‰®oŸôý¨ D„8òÈ#á‡; pfeçÈâÐÇp@äC ííoI×tÁýÊ+¯Œ1ˆ^Ï[™ž›<0z¡óCÅk™P%«­¶ZŹ ¾TGæ1¤|é=\ÔѨˆÊÅŠ¾ML«Ã;¿§Œ8êt(pŒxÝóƒâc“¯FFôÊÙÄ=U]•à Áö÷ &Ü\_·aDtrÐÙš‡cáØfuÖtWé:e´ÊrCg qæ9&âb†ˆ˜d61lüpŽ.¿üòªÒVµQõˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@³òxøðáq^ÅÇ{,¸ã*õM;í´á·¿ým íóäy;Árï½÷F¡9ûÚÑW<ñf›m– îÅü^N³—Do ÊXÑQ´XWyËéÌwô2"QœtÒIÑ ¿ÅÇdœ€O;í´(°?üðÃyñèœDá<¹XŸïÔJ¯hºà~üñÇÇ ‚‹š!½WîbnQpÇ#¹žóœ^Ì=¹}bLz}ˆÛÄÙ¨Í6ÛlOmï¡«•:ç꧘bŠZÉN0Y(? z¬`Rôâ~ï½÷jæïÍüˆéEch=mÄ®÷p2ÜH]`÷6Òq@ˆÒuÔQ¾¹m–´—02|0Î ÇÃ$¸O=õT>E'Â6ÛlÓ6mVCD@D@D@D@D@D@D@ÚŸ@YLî²VÏ2Ë,¥ñ¹ ÜQçž{nàSÏ>ùä“z»c(æûï¿?:Ëš}‹6!òºkYƒ gžyfü u!Ö£!Á­£ö{ºt¹ÝvÛ>=eçŸ~àÓ¨šº3<9OÔ]œ1m!yÒ°=é>ÖÑ¥øÀ™ÔÌýˆ–ˆ³.ü²ö#0F³›ä=,÷ÜsO>¥^.–“ï¢Ýwß}qBËâvÿ~óÍ7ûj.¸wg¶á9æ˜#–ç!Gò“D~¼÷ wÃpœzF {ÍsS,ŠíäýàƒêÑkûðh÷‰c=DŒ îe½„θüõŒxýp#ÌK³ vbäó!¬Ojô|rÄ÷â†ç»LD@D@D@D@D@D@D@FgD` 8axgši¦ºb{‘”’/Û‹iúÚ÷îðhֱ‘þùç—ØÞ,Ð=PnÓw&9Å;RŒùTÖþ+®¸"nf¸‰O¦#&ÓÉ'ŸœnÊ×ñ`wÁ›Øð ‹Áº3Û°Ç‹bPB”ÙÅ_œ‹ä…¬yõÕWcˆ×ÓO?}Yq‰*ܺÒèy›±$¬ Æ$·„üa2 BÆ,¸à‚UÕm»í¶q![ˆS_fx’#â3ëuÚÙR–¶+ÛŠüÀpbŒ1ì©Ì6ÅõƒM8á„eI´MD@D@D@D@D@D@D@D@D@ª4]pÇc˜I 0ø;‰¸PEc¶`ỷŸ]+4ËÑG9äÜSœ²"ÃDxÒc‡~xEˆfFÄGü%ݨQ£b:þ"…xIîµãØ|óÍñç1ÚDüòTÄEt>öØcãþ%–X",¹ä’q½Öz 1b )-ë³Ï> {ì±GÅp ¶õ¤19+!rù”yÚsŒxýSÎN;í›VæÝÎŽ¥–Z*Æ’â „ÀíÇË’ë€2ˆµ§?!kzÊ|²]bˆqm0 :¹ˆßŽ1gÀm·Ý×ýióóä“OÆMžÖ÷k)" " " " " " " " " 5 ˜ÀØt3q6[tÑE3kDþ±a&™‰·™ š™ÅxÊLÍ÷­·ÞzÙ?þXÑ.íãþ•W^9³8Rq݆wd&†gº¤"ÿ^{í•ÙäªùùbwfñÛc^ê³5™ ÅÈ,–w^·‰ñUyMÎ,.RžÆÄÜl™e–‰ù½Ý“M6Yf^üun¹å–1Ïlo·8K™ ÖyY”»âŠ+fæQq<0²Ž‡ÌâÏÇu8YçAž¿¬Ì|gg–žƒŽÖ-–~i‰ÇsLÞ~‹ç•Ùä¥éØh±ï+νŖÊläAÆqyýÊ%³Ž‹Š2L¬û8à€Šíéo‡…óI7Çõ­·Þ:/ßë1ñ=î³xW™zÈ÷³ÎuÈ'm—ŞϬc¤ªìvß`£1â±M9å”íÞTµOD@D@D@D@D@D@z”À.»ì߉;ì°-W…‰€ˆ@¾624ÝÃJ&˜`‚~䪫®Š1†Ø†÷4j#ûùçŸÞÇx‡_vÙeáꫯÄJ*3ÂÃàÅn"vÜ—4ÞÚx.ã=N^¼äù^4f&<õÒ… FŽcËFäôÓOþóŸ«ò.å‰'žˆ˜°=âï¾û $˜¸€3ÄpïÈQ‚—ýꫯ“j‡Øå/¾øb`fb<èO9å”`‚qÜ'CÓQÙ­ÚG»OœaRµŒósÎã„LìÀäî=ÏèF x¬ÿZåtv»‰ñq²ÖÉ'Ÿ´‹óÀ$¯L°;pà@Ϧ¥ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€Ô%0e¾nŠ&ìdV]Dd>TÏd ˆœõëu×]7†$!.ø\[…xK pÄõX Œ?þø ·–0&™}9-˜ëši·yͧ»^OÅtâ Ó1‘Úk¯½>ÿüó8!k­ˆ4}_X'”ÏÓO?&žxâ0Ûl³õzŒt&°5/ü`^úñŽ—Á6ËsY‡M_àK逢óÉ<ÜÇ~ØWš­vŠ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@· 0gÛÙgŸÌÃ=†îv*@D@'ðiŠã—»‘7^H—R"TóYz饻”ß3Yx’'Ü¿wf‰€íñÔ;“´ž%~:›¯˜Þ—ÔÁy–Yf)féóßñ8gæêv1&QµFñÓ.mR;D@D@D@D@D@D@D@D@D@ú&–„”é›hÔjhœ€÷ÆY)¥ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€Ô$ Á½&íÆ ôJ ÷Æ›÷)÷ßÿ°Ùf›…Ñ1®ùÿ¥ÖD@D@D@D@D@D@D@D@D@D@ú*>#¸>¼¯2V»E@D@D@D@D@D@D@D@D@D@ú…”é'Y‡(" " " " " " " " " "Ð|Ü›ÏX5ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ôÜûÁIÖ!Š€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€4Ÿ€÷æ3V " " " " " " " " " " ý€€÷~p’uˆ" " " " " " " " " " Í' Á½ùŒUƒˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@? Á½œd¢ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ó Hpo>cÕ " " " " " " " " " "ÐHpï'Y‡(" " " " " " " " " "Ð|Ü›ÏX5ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ôÜûÁIÖ!Š€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€4Ÿ€÷æ3V " " " " " " " " " " ý€€÷~p’uˆ" " " " " " " " " " Í' Á½ùŒUƒˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@? Á½œd¢ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ó Hpo>cÕ " " " " " " " " " "ÐHpï'Y‡(" " " " " " " " " "Ð|Ü›ÏX5ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ôÜûÁIÖ!Š€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€4Ÿ€÷æ3V " " " " " " " " " " ý€€÷~p’uˆ" " " " " " " " " " Í' Á½ùŒUƒˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@? Á½œd¢ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ó Hpo>cÕ " " " " " " " " " "ÐHpï'Y‡(" " " " " " " " " "Ð|Ü›ÏX5ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ôÜûÁIÖ!Š€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€4Ÿ€÷æ3V " " " " " " " " " " ý€€÷~p’uˆ" " " " " " " " " " Í' Á½ùŒUƒˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@? Á½œd¢ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ó Hpo>cÕ " " " " " " " "ÐãŽ;n˜d’I AƒZP›ªj2³êÍÚ""ÐW <ûì³aèСaÊ)§ ~øa_= µ[D@D@D@D@D@D@D@D@úo 0¾<ÜûÒ)S[E@D@D@D@D@D@D@D@D@D@Ú–€÷¶=5j˜ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@_" Á½/-µUD@D@D@D@D@D@D@D@D@D m HpoÛS£†‰€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ô%ÜûÒÙR[E@D@D@D@D@D@D@êȲ,|úé§á«¯¾*M×ÑþÒLÚØ/èÚè™ÓüÍ7ßÄßàÏ?ÿÜ3ö@)œ[™´ŠÀX­ªHõˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ô,>ú(¼öÚkaâ‰'sÎ9gÏÞGKƒÉ³Ï>f›m¶0ÁTEGû«2ÔØðÅ_„çŸ> 4(Ì?ÿü5RUnnõù¢ãᥗ^ ã?~:thecô­Š@zmü÷¿ÿíôù­*°›¸Žé8âZž|òÉ»YZë²?ýôÓáÇ K.¹d`~Ž9æ“L2Iëa5!øsüðÃÃ?üÆwܰøâ‹·´ í\ÙgŸ}¸q9æ˜a¼ñÆ SM5Ugœqê6›<äýúë¯Ã€âýeÒI'-½ßÖ*ˆ{' sÏ=w­$ùvê5jTøöÛoóvv¶¾¼°­HpohU#" " " " " " =Mà§Ÿ~Šb¢og !êñÇYZh¡0Æ•à;Úß™ºZöÝwßÇ3Í4Ó”VÝÑþÒL%a„X´zìºz¾Šu4úÝÛˆ˜&ë˜@zm B–ߎKé¹ßÿ}l籯B,Üf˜a†ø;üî»ïzí^yå•À9Å‘ý>Wï7ÚW8w§Ü‡^xá…@SÑè ˜i¦™ÂàÁƒ‹»Ptà½÷Þ{UûÞgœqÆ0묳Ví+nøòË/Ãûï¿ß¡°O}t:}üñÇÅ"¢Ð?í´Ó†!C†Äõª½¼A‚{/ŸU/" " " " " " ­&€g!¢Vj¡£ý­no£õá‰à7å”S†±Ç»*[Gû«2ÔÙ0ÖXc¼,‹Þ }•]Cí»Š×F­óÛJŒÐ@$8p`+«íV].Æ"†ö¶¹P‹wýtÓM—7§¿ÿFŸzê©ðùçŸÇ{äÌ3ÏGH1"s‡ÿúë¯Çk.e<¶“ÆÅõÉ&›,þÿ o¾ùfÌGgK-£3½£S€sÈo‘NFH0RmŒZ Nî¿C»™÷v;#jˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@—¸ØWм°Žö{ºF–N8aX`Iª4}€@ñÚh‡ó‹÷n_2<§_ qE£Þ4DuÄY¬/…ãi63ÄjÄvF½0º‰02nè/¿ürxûí·£Òû(£-Þzë­˜”Œ´C…ŽG:…äñg_:ªq¡ÝCÑà¹Þ‘ÑðÁÄd„IÏáSLø}âmO›ÚQp¯3ÖÑÑj¿ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@[ \±uHZiˆ(Ô‹ÐÕ¨ÑFBw°ìL¾²òÉO˜â4#­£ýžžãhe(‘Þ8_„ùèéëƒòàÖˆ˜æ¬D»ÊnõòÒ¼ÖiO£×†·;]’—v 6·ÚüÚélýÝi3yáZv¼¤ðNÅØ2&¤éì½ÂËéJ^<²»kÀw™Á‚ãáCºFÍÅ;ÉGÎ7ç¡+æ"6!·R±ÝËrñÚ¯-ߎPOÜ_ËÎ/èŒ*"ß'Ÿ|âÙâÏwÄx¶ÓþF qû=Û=/¢;{îgífòpo·3¢öˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ 0âÈär.j V0¼щp+Ï<óLEé<ð@üÎdy„b©·ß=n‰ÿޏ4Ï<óDoHw+ å ÑñâTh¢­´a–Yf© 3räÈ(d!ö,¼ðÂÅ"ãw!Ê#™u´Qï\Ž ~´O&£}î¹ç¢Wè|óÍ&šh¢X¼³ô6ù÷´î”­³óýœ/O‹Hƹ¥#æe–ž“²މ ˆ^. Ž0 ´ãí¬q x™â1ëeRe¶Ðe“d’Žcù1¼a ŸÂ¹+ËóÐCÅs»Øb‹E/Y®:j¸–üºÁëc¼\åzœzê©Ãì³Ï^áuÿÿ?e׆ŸO?¿izÖñÚŻ֯¶‘q8Ö/œíx ¿ñÆ1üÅ¢‹.ʦ*#ÄÓ“O>ÛÈÄžü^ ýðÈ5è#áÍq2)njxýÖúÝ‘®3m&½·Ûï!üF8fÎá¼óÎ[Ñ&ÒâÃÏߋƵי{EzMs®ÉËùðù)¼~®Œk‹óšZÙoô?ÿùO¼ ><^G/¾øbÌÇ5šž¿wÞy'ðáÞ—çk™ûG™‘‡ûmå¸0Äêé§Ÿ>Þ¯Ë~‹Ü›¸†<i¸á…ž^eõ¥Ûü7Wö;óvP¶ Ù>RÁ¹•Ý[ÈÇõÎÈ~K¤MïýÜ_Òxq.ë¿%ŒûŒŠ&û÷Žv ¹Ôù»i<ýhˆO<ñD‡ðZdUD9D„9D ŸÌñÂEÚïëéÒ×Ëö³rÝð7I…h„@ˆxˆpøçõ’{úé§c§€ 4ˆ%åäC°CH]pÁ+KêâS—ýK ylá2ïKÒ p!Ú÷ÓvuŽCè!í`mƒõ³t#_±Mlããæë¾ôí9_äñúSÑÊËò¥Ÿ“´¾úsÂà(ì“Yrþ`OgB™Ç«—Q\"ö‹œKDFÄ/®;êáz¤s ÞØGD?ò "îq½°é("qñ:¥|®e ¡#I°F|NÅvös¿ãþĵ(ß]ãÚ¤ƒ sÁßËä^‰ñ[ªe¾ÏÓ$;`ð£IDATÖJ×èvŽ×G¥ð;¥3‹N%Äv8sMï#–ÛŠtµ)µ¢vÕ!" " " " " " "ÐmˆîEžÆvÄ1¬§¼Óò|R±5ÝçB5âB+æâmÁs¼Ø&DLb$wä…™ÖÃ:^ ˆ•`eVk?b0Ùt¤õiÅPé¾®®·ú|!„Õ:¸¹øW&—£ ‚”™ ¯žQ ´. {Dj.=½/61 ã—äO™÷7ÇæžµÅ²²ßë+k]Iµù*B+×,×|­k”ß› ®~ÌÀõE>„u~©9wÏ—î+®ûo៑ ©qœü~øøï²;mö²9ǰeçš4x#׺ÆHÓÙ{yܺ“×ËèÊ’s–vbz´‡øîŒ)â· ò¬»ùyàyç‹ïó%£øÍ"£Œëkª¬¤qæ\Cœƒ®í¤cA›uîÝÅ6¦ÇÒ•:º’‡NFàP7÷)Î÷*î×ë£>^·Ñíh )ÓŽgEmNH‡þ³¹HÖ Á¤èq™ÖH„¸Dø÷ÞEhFpÅ«EÄNçaÎx´#¬³­h~-p~j];lO…u?wÜC ­R˸¾ùÍбY<ÿµòøv:ICÛ©ŸÑ)îiïiXz›Ë:­<ÿ©õ{ót,ùЀÑÙC'€—KÃÿw:›Sn”ßì4Ü›MXå‹€ˆ€ˆ€ˆ€ˆ€ˆ€ˆ@“ ¸ÒäjªŠ¯%€zBö#¥Ô!C¢@BBU øð!ä"Þ£,bB ùʬÞ~oW-¡ÖËëiÁ½Ñcóú»»lä?ŽZûÛa{=ÏÓŽÚïûG½ãALCð¢>Ñë¥gyþ_{ç²*Õцámˆƒ©‘ˆâYpæµeäoÈâ TfâÌ ‚&ý¬ðnÊÚµzUw¯n;{?íê½êü¬¯*ÿÿÖ×ß⃠—¾ÆêDXl=ϱ:ëÜgËl§Õfø¬;<Ñ9P@|Åî #”÷x·gL!ªó¡­ˆŽ´…‰ç;±ÇiÓ1ÓgÚHÿå5±ÃǼRvŠYò[}%/mÕ×ä·êÖeWù{¬=j‰ÅA¿8à— Ý9¸#¿ܓ׻fgÖvƒÀ<•°­žÄ:ÄF8ðÄ–ˆ)?eŒ/ýxÝ·úI^ÆÝ*Ó{/caÂhî±ý±_8õö7w9c¸ÏMÔö$  H@€$  H@'„âÞXBŒ¸!¡‡{ =€p‹˜Dìb^Šš¸ïľžÁéa‹¶ÆÄ¾©üxs21ÁAg,olîsßO(…eíÆƒ¶U¦|­ü<Ç<§V™ò^B½D+óòïÙ/^ ψ{%딩¯ˆí±™”¯ËÌõ÷”m´úɘcírá’òe™ÄÆŽÐΕu°,KêÓ'|ˆû<3Ú䥚7oÞTì•,Rưɘ‡†ÿÐÂ2^Ý˾P56Öhf84ÈœZ6¸IÝV›ÞËËOyI-B8{XuÚníY3¥[c@ÐfÍ †ÓF¼ÕaÃ÷±}óéõ,ØÎ3ã×Sb;cŶ[ãO^ÆÝ*Ó{ƒRÏÞ×S¦·ß¹Ê)¸ÏEÒv$  H@€$  H@'Œ¢eK\y$„¦ˆMïÞ½;¸{÷î×VŠàN^¯àŽ7å˜$cX–O…„‹©=R3><îtŠ˜‡Wt‹7coÝϸɋ'rî劘ñ·õâÝ”+¯ ß0Ö&Âù‡†PñvMÛ„ŠH色M¾¢ƒ„¸›nláŸ)Ûhuɼc/ yQ—ãp#bwæ\–‰¸ sÄWDJ¿ä—eëï<+ÖϽ{÷¾ÝSÖî³~æsÚ¯¯ØcÊ‹;ëüòïU÷йê–íÌñµÑåè'/|óæÍ v¥>ÂÚÛ·o1ºä‘ü±kY¶Åv¬Þ²û°`ŽpÎAFÊ#ÄñÁ©„Çy}¨Ð\¸ã}¶ZÜÉ#|Þ´¯_¿NñáŠöû÷•ó̉ÃLˆ˜ð2Ä©Œ½dnÄðßfê±VÿØKâI3Çš'Ê/_¾æŽ`Xó¤M8sÐƒíæ¹E„oõYÞ£„RS·ö²‡)Â'‰>HsŒyh¨ñÏ ;hͳ.¾ê^QÖߤnÙN¾o²F© SRß´ÛÏýò`‰µ_â°fêýµÄ¾HŠ= ¶ÇÞ^½zu¤¡tbC=Ï€¶s¨Å¾Lû½‰Ãl—}‘ñ'Œõ±eöOv±|¸±æ?98ÂÆêƒ½pfó}:ˆ‚ñdFpŠUÇãETFt+ExDDèˆXS®Sù)‡ Eßxrx€( î!.‘Oÿ™-a?íäÚË.å{®0‚[ˆÓÙ¼Œ ïS„Ø1oÐ0FèF(ÆëÁ Þˆøˆ¼0±ž_‹;ãå9c+HÀ‘6©Oì çÏŸ?<¼A˜=wîÜÂ!å>|8<D?ê01±™ë6S¯m´ÆÀ ãÅ^à‰'?¶$p‘ûçXB $T˜¼‡ÇÊ—÷‰·ÍAB$õ±Úàyb£eGöž…’iÈ‹÷xî#È^¸pað²D0¤NYá„:¥X˜ºå•±"6âu‰¸Y§©ü²Ö/©‡ë2åß«îsÕ-Û)¿o²FóSÖ vÌ'6ÌþÆ>Æ=lƒçC_$žÉõëס›5ˆṗijc¯)ÃjqŸgwéÒ¥á`'"~&Y¿ðÇ[½Ç†RvÙC¦öY&lïÚµkÃú¥-HÌ5Ï>¾¬·îwÚ£]þûÂAA hñ_Â0¦}L§'ÿìãÀ“$°NOÙàù†åx×kÍZ€$  H@€ŽÀ+aá/Þ¹ˆ!ˆ™x¡—bßÑÚÿÝAŽÀ+”b^Óx—÷$Ä"Ä„-„Ú:Må×åË¿Bb‰¿‰›çò­[·º„­²½¹¿36XGà[µ}æÈüZìVmò0¢M‚[Ø 7–üÃaO<²{Ã%Mugmb­NÂûå³ËÄzcý0†ü2»˜JsŒ™Ú)Á2Õoò{÷Š9ö™ô¹+ódž±ÖRmÃì‘d¾ð«êS¦×vX/ðc¥^ÏóÞÆÜË}ˆ5_Ï}î>™7¬˜ï*{ÌÜãèhïËbŒ¿Ž·u´` H@€$  H@€$2ÜK„#>«&ꌅ’¡­©üô‡P–È¿-B2 Œ1®R0ްH›Û•2®eWÆP{Ž/+_ç1·¹C0Àl1Íé [Ïsìï^Û«_ßßϺ±¿]{·êúsŒ'¿uÓª{EÙÏ&uËvæøÇev¿,þ9$é=dÌxÙ£æ^¿i{•ë¦ûÐ*}Q–yÏ~Õ~×-¯à¾.9ëI@€$  H@€$ð¿&€PLhÂ?ƒR&¼¶ç¸õP–ó»$  H ÜC«$  H@€$  H@'Žñ®y %1Üy1!qÆñ\%1² geo¬êÐ K@ÀwÜ¿Ãá€$  H@€$  Làçý„²Ø‡+ScÊç¥|W¯^^BÉ EÞ“ˆ«Ì ñnÇÞ$ ìŽÀqÚgvGÍžö€/M݇§à$0#_š:#L›’€$  H@€N^ÈK(¹îâE€' ®“•€$pü øÒÔãÿŒ¡$  H@€$  H@½ðØÿ/òìŸå$  H`ÿ ü´ÿCt„€$  H@€$  H@˜&pûöíƒ+W®ܹsgº°%$  l€‚û Ú¤$  H@€$  H@Àî |üøñàùóçŸ>}Ú}çö( H`A@Á]3€$  H@€$  H@€$  Ì@@Á}ˆ6! H@€$  H@€$  H@Pp×$  H@€$  H@€$  H@3PpŸ¢MH@€$  H@€$  H@€ܵ H@€$  H@€$  H@À Üg€h€$  H@€$  H@€$ Ÿ¾ˆA8>¾}ûvj1›_þY¤Åõ¯ã33g" H@€$  H@XNàëׯ§%N/®/®|L€vE@}W¤íG;&p~ÑbûŸ;î×î$  H@€$  H@?šÀ‹ðÿ‰ÿѱ HàdøYØ´ô©ó¤IEND®B`‚bpftrace-0.23.2/images/ci_appimage_artifact.png000066400000000000000000001701541477746507000215030ustar00rootroot00000000000000‰PNG  IHDR†¿“ØhsBIT|dˆtEXtSoftwaregnome-screenshotï¿>-tEXtCreation TimeFri 17 Nov 2023 09:32:58 AM MSTÄX" IDATxœìÝwtÕÛÀñïì&›Þ+ „z'¡÷ª( " ŠXPDPü‰ åµ슂 ÒQŠHGz¡÷z!½'›ìîÌûG ©PĘçsŽçÈîÌ»7SîsÛ(š¦i!„B!„¨²tw;B!„B!î. …B!„¢Š“ÀP!„B!ª8 …B!„¢Š“ÀP!„B!ª8 …B!„¢Š³º‰¤gIIË 3+›LcöíHR!„B!D{[ìílpqrÀÑÞö¶§¯ÜÊ{ Ó2²HHJCÕTíl±··½#™B!„Bˆª,=ÓHf¦‘ô,#:E‡‡›Nv·-ý› ã“RIÏ4â`g‹“ƒ-ÖVVèt Š¢Ü¶Ì !„B!„MÓPU “ÙLZ†‘Œ,#Žö¶xº9ß–ôo*0ŒOJ%3ˈ»«3v6ôz™ª(„B!„ÿ‹E%+;‡ÄäTìínOpXáˆ.-#‹ôÌÜ ÐÞÖF‚B!„B!„øéõ:ìmmpwu&=ÓHZFÖ-§Yá¨.!) ;[ìl èt2lT!„B!þi:‚;[’Òn=½ŠlœžiDÕTœl¥§P!„B!î"½^‡“ƒ-ª¦’ži¼¥´*Ý¥¤eàh—»ÐŒB!„Bˆ»ËÚÊ G;[RÒ2n) †™YÙØÛÛÊR!„B!„øÐéìímÉ̺µ÷ÉW¨ë/Ó˜-ï)B!„Bˆ EQp´·%Óxk¡LB!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨â$0B!„Bˆ*NC!„B!„¨âªn`¨^få—òæ´ÝĪw;3ÿ<5n+ÿ7ž K/b¹åÔr8¹äK^ùp!ûÒ´Û»Ëþx·Bˆÿ •Ôsá¬Ù¸ÃçcIÌ0¡Ø8àáãGú-éÑ£)þ6 Ù‘Ø”àOïæÞ7ßb¬%slë) -C©ë¤Ü¶_!„âŸQ¹CÅ‘†Ýûa(è•R‰;´‘ÍçmiÚ³õóDz|üoðhÓ0ðÍ (ÈãëVhôÈL¬ Ha¥eq~Ëb~]q’T]鑟â\—ž÷YSjg«9šÝëëæ†[ÕíßBT˜Fú±?ùü—=$:вuG:8@fj ñÑ9t ’ö½›åmk"bû Vf?@Ï[ µ„#¬X¾ŸÆÁ! !D%TÉC{[u °à §âw°å‚uÛt §WgÞF– &°kÖ¯Ì9d&¸ßÓÜwu63•ÜLq ¤CÏÀRÐH ŸÅ‹-º¶ÂSÊWQ^Z {6î'Ϊ6¾ü Ý<Š>- ú¼ZÖ9œÌ„ [9 …¸#ǹdÆ·’ŒBˆ»¦r†bæÐìù9±7¾Ð¨¿—ñ×®3\Ír£ç‹cy0 ’?>ú™Í.÷òþØNxåWºM Z·†UáD¥š±qó§IÛôoï7àõôžj|­ØÔ”³¬[þ7ÛŽ]&1Ç ¿º´íÑ›.¦U¼½@屟¤½ýµ½)îë×ne×É+Äg¨ØyT§iû èZ׼綳™‰Ÿm£ÆsãyÐ~/‹þÜÂþK)Ðà!>z¦ŽJùÒ@3¾Ž¥›p&&³Á…šM:0°³ëëŸË꯿eiRs^xg0Íl ©‘±oÏ™t‹ßZ„öº—ûšy\÷øjÌf&NÜBµgÆÑ#m=‹6æ\‚ÅÞƒ &mé߯=uò[¤µ6OþŒùÑ­ûÁC4,<Ü2oøñ²œÎŒß—šºÜs#îèVVl:ÈñÈÒrÀÆÑj5ëСï=t °¹Ö` @æ¹Ìý{7‡ÎÅ“®p­V‡¶½ïáÞ&î׿€t.Ô nÄ=m:roÍ4_oë’,WؼáFŸ.ôjb_ŽF ZêiÖ,ÙÄÎãQÄg‚‡?ÛõâÁnµq)R/TI9»‹•ëörøB©&+=«Ó8¤#÷t©—õµí¢VO⣵*}ǽÌÀ€Â‰X8³ä ¾ÜæÂ  ÏÓÓCá†×] Vþ²/¦BçS¯csÂ)Ö¯ÝÊîQÄf¨Ø8{Ô¸5½{µ¡ž‹Œß•–Ez¦ŠâàKu×’¦z½ÐÈ8±ŠïfïàbºŠ–4›ö(¸t|†O×Å •ä³{ظó(§.Å›”FŽÞŸ Æô¼¿7íümQÔ«lþu&‹&bÒà¯OÇó€¾CÞyŽn®©»G«©œÚºž5»Oq>6£jƒ»5ë4§ïý v”V2!„¸þÃa.55ŠÍó·³åœ+-Zw¦¥#uËêYÔRÙ3÷g~=MµæÔÐëÌXN„ÿΗ{lɰ€WáÍ3#øý‡ßØœèBãN}¸ÇÏŽìøó„/ú™ýv­z‘ä-±aLùî/NÛÖ£{ïÔt´:œ¿—M'"ö Þ|¤>Ï;ÍHìñ¿ù!|/Æ:MéÚËkŸêØ+IÇLÔ†|¹üV!ÜópîJQÇ÷0uz: eœÎ‹ÐК¬X|‚ðY4mnw­Â®epxï)² uh×Â¥¬åk’Ï~ÆDÆm ñvÅÁÇÍ›YóÛohcÇò`àN?#§–Oå„Åö}hç)±içr¾¹ÌØ—ï%ضâ„ìÓ+øæ—pÔàô€«’Eb\4g#RÉ1X LÔÄ0¦Í°" MBÑ¥_&|óNVNÁ<ö¥ü+üÚ÷Ã3Z…æ_j¤ÞÂÖk>Ò–åŠM®°ê§…Ø×¥Û€68¨)DìÞÆ¶e¿§¼È«Ýó‡ˆi$îÿ/fÆX­)]ú¶ÇÇÞBÒ¹ClYùNöcÜÈNT¿nËÁõ•uÝeŸþ«Üe_Byϧ ^Çæ˜0¦Lú‹J ÚuìK/wkŒñg Û¾œoŸeøØÇhç!ãxE%£ó ¨†Úîcl;܉Z-\)yQ0TkÁÃOذìçõœ«Ý‹Ñ}‚ÐÖ.Õ ¶Ï:wˆ#iî4mÛˆnÁæu;™õsN㢉­3õ{ âQûÅÌÞ£ÐîÑi례Ο )µpií &­M¥fûÎ<ÜË ëìTâ¢#9yÕˆÞF‚B!„¸Sþó¡w€ÍöÝyù^Ô¶+ô@)%–1ÝÌŸÓpmó¯i€CÞæÛ7`áW?³žÂÝf*W¶®bk¬¦CžcT[×¼Jwk:·ØÌ—_­¡ÈŒ2-=­á„ZŸá/>A;×¼Ä[4ÆOý†)»×²¥K0ýªåW@ÍœÜzÖ½ÈÓ!n×þPZ:aåLGK?²uÉñíÌØÑ÷R+/ mÓ‚ÀÙßòsBÑ rQ î-ZÓpÅBŽì9Az³–tÐ¥c÷éì·¦ÅuúÖµû1þõ¢Ÿµ0sù‹M>ÃÀ@ÿÌeQIJõâ©ñÓÖ9ï8¡­iæ1•OþÚÉÒ]my­›G‡«Äž9O¢À Gǵ½{•¶uЉÚÏŒæÉ‚»æ„)|øÍ6öˆb@``)•­Û@eë†cd¸†Ð»•Kù~£)]³‘¼20°àL mîƒùãßÛœØ®Þøê€¬“,[rˆdïÎŒ{/u 6%¤ú >^¸–;2®KEËöšÒ¯;•È ”}qå=Ÿ*tk©ì^ºšcÙ5xpܳô©–¥µ¦SS¾øf=üu˜ÆO5G¦L‰ÊÅ@£{Òñüïlÿí.î ¡k»Ö„4ðűÐMËÚÕºŽ 8ê@çèCpÝZÅ*:ªõzŽw‹\¤ ¶ŠcÂ⓺¨Ò¤¾=¾µ‚0Ÿ2€¢àY3ˆzÕ ÝÝ+Ò(¦¥qöL ¯Î<2¨KÞ(\ý*òó…BTØ¿\çJûþÝ‹…¥²y쉸ҡ^Ae›´oå[´°Ô$ŽAulHç–®E¾³òmEû }‘Jµ–ÁþSFl‚›ÑÈÎDvvNî9z‚‚kbPc‰8ŸQèù©`]§ƒZ»yHW$ÓùÓœ6ê¨J`á®AÅ&!õoXÑUÒ®‘=Ù§r(5?g av¦e›zØW°²¬s÷ÀC¯‘™‘Åߢ`ܜ΅¢Ç·M(õ¬-\)!„¸Kþó=†è¼©áWž>•¤„4]mªyvtxz¸¡#éÚGZ2qI*:o|Š—¢b§‡-œ-´yR¥ôDZyRÍC‡š˜@¼ 7ÛZêuW±²/®|çSÅ®c-9‘³‚‹%F%+vøú8¡œN$.Y»ÿ~;šø2xÒ¼÷#4ï9€ØˆClÝ´…-ËgpäX_^Õ•š7lx²|v/váä¥8Ó˜,*šj&]ÅzËÅŠº÷=É“–%,Ù47;Ô¤íÛ·¡m°{ÙÓ„Bܲÿ~`¨èÑëÊ9ä>ßJ[«T§/^)Ì{* ¥·ÓéŠUŠ5 ÐáÚr#:û–RÙW°õpA½Pz½¾dÒH'7 J)¿_ÑéÊ5LÐP·!{Ø´÷0±»àuõ á‘~}ZQë:Aƒ³“¾]F„M0={ßÏ€wm­Ð2ûëÕ\-DZ´R'æ©äÆdåù*ZñøM±#°Ó`ÞnÓ‹3÷²}×^ÖÎÙǺõ­6òAB E‚Š^O9OŸÛ&ûÌ66\°P­g'šV¤K¶œçºš_¦eTè4 Ptå H-j‰”•— ”}‘‘ؽ‡aa̼5Mû3ú‰6øßÂüg!„eûï†å¦ÃÕÍ EM$&Aû"ÎHMM/ZwT\ðpÑ¡&Äoï¢K"’š–U$uÅÍ+8£žjA7=_©"é8¸¸`à"qq©h¸]T%5•T­c‰õ´kíÆ¿±7¦÷æŠ.€‡Z—”¤ÎŰœÊr£ËÓÃx°îµÂÑ’*òD×HMH"›@ì šGtš†®š ¹S,uèt ¨ZÉú½–FRš v%SW nÔ íEÝÐn 8²œï¦ïfÁÚ&4Rïú+¶ÞIZ2»×ï'ÑP—aüïÀªàì幓ŒÚسèßÑË•Ÿ;ùë­èt *%c@))™h¸V<*ûŠœO»Ž7O¼¬4ŽÇÄ’©Õ(z=i™D_MC³ªŽ—‹ôŠÿņšMêâ¶1–ÄÄ44®j‰ìÙzŒd»&<÷lZj¨ÊŽ*sÙ±RÜÜ=ÚÊ9€^„ôèÍéU3ù~Ý þ:Ø„Q!åY¥Y!DEIm§€ž êâB{ÃÎ’Uøé¥Æ³ÿ@Tч Îƒ†õ=QÒŽ±ýHZ‘‡–rŒ=¦¢Pûz´ ¶Åtz;kÏÝüÜªŠ¤cT—º6*çÃ÷Ud>I6û“X®‘:üZ·$H¹ÊÁC‡Ø{8뺭 ¹îKõ4²sLhŠnE–ú×H>yŠKå©‘q€}I…¥‰È»‰0[Q«AíÜ9ŽŠ-.N0Æs5¥h‰dŸ=ÆñbKöBZá^¿!µì!;Ëx‡æ –éÂÖ6áÒ™âãˆo«ZMiáªq),Œ³ÆÂea!6<Œ#YV5m@î¨T''tZ1Åæjé§8xÆT¡sùæÊ¾"çSÅ®cÅ®.­Øa:½›mÑE_'bŠ cëY öõSß¶?RˆÍLŽ©´«ÓÄåSçHÖ ø '·Â` –Œ 2‹\ä9dgk(¶Î¸^ TËäı‹OÞ`°FѲHË(öE…îÑZÉœuŽÔnR 7ÅBVVNy~½Bˆ› =†…‚»1 ÑqfmŸÇׯtjà…!;³û÷p<ÅÙ…¶ÖÐõÚï›ÍŽùSQ¯´£¹Ÿ=æäHìy*qZìaÈ}]ŶDÚ7bØ€¦²"©¨|ŒGøåƒµ¤àå‚‹³K1玱ïd<6uï£_“¼×éý ²!ìĦ/SiëoM¶m :5ö¢~°+·dùÚ@´òÅ&+–“aÙpÞªØâc:ÜkâÁ6v.Z„K×`Ü4ðlÜœ:N¸Gk)lùu&‡ìëR×ÏOW[Ô´ŽìØE¬M Ý8Ko¡BÜ!¦s£ýð‘Ø¬]Ú½Ûù}¿ k—j4lÓŸÜ÷ðɼsE6WðØKÃñ\¾;Vq8G“w¡÷ §cįü^4y½W#_ugËß[Ùyt‹wd£ZÙââåO½æÍ¨QοFùÓ±¦fï§çô7K·aõÂ=XlÜlÚ‘çÆø±ÿ»©,ÏgZµ©Ç’YGHqjMûF7Æ£`ßdc¶fѦüôÕ*°uÁ¿^k}/)|™ûòã×ÐÁ<æt?7¯ak|6:jµÀ€~m©esm;û†÷óÒc6,Ùx€¹S¶aÒÛáY½!¤ëÑ©ü|îÚ¶nµ‚ñ9½ŸÝö“b´ X;àY½6}žìIŸæNw­ÒaŽÚÅßÇ8¶èD‡²Þµy[(87y7^ `õúpÂמ 9[ÁÞ߆}žà¾î ‹ V\ZòäÈþ\¹‹s¦²Ìb³Wu·y”×Ûmçã™å>îÍ•}ϧ ^Ç:Ö<ûŠ ›×mc箵„§š°rô$¨é=¼Ü§õÝd`…¨„ ~´ `Ûé‹8›JfŽŠbm›omö§OÇ`<òŸŠ3íJü«Ù¾}9³4;¼;¥CcoêÞ7œ§XÉê øìoƒ“7Á­»1æe+V<ŸÂíŸVA½98‹뎲â÷ÃX»ÔãÁºÍ¨ã¤+ÿ=Z±'¨~uí<Ìúý©dšÁÚÁÿZmxâ±î´»£÷F!„¨Ú­ôÕ=JuèäyšÕº“ùù—ÒHÞþ o-ЧÓè×R÷FK5æ°ï·˜z´#?}Œ–2Q¾ÂÔ˜ÍLœ¸c÷Ѽwuó,nƒŠ^ÇB!„•Ç­ÆjRß.KûDb±®FMßYîÃô~Õ©.uO!þ*x !„BT%2”´0Ëyþüa-qþµ òuÃÅÞ͘ș½»ØqVÅ¿GWZšl¤FoeÊ‚ ¸Ö©¿— Ž6 9I‘ìÛ¾›c™n´Òõ"Ä?¬‚×±B!„À°(;Á <ˆ|´_j¶UAVem‹2¦ñ¿ÿ»À§çÿïÒ¨´åzÛ¨D­œÏúÄü»²ÇFƒøpÚ|V._Ȣ߾å“W£OSjöç±Î·çy*„¢òªTs 322 š¹ÕžÂâÜÝÝHII!3#“ŒŒ Y­´¼2N°3<†Œl[ÂΣöñª|­ BÜE¦g\uCÆ ™ìËѸç:ž–÷>G}9o0•µ\o çNÇœ_ú žøè]†5Éì7¦uÇ> }a<&ì ÍÉÌøk -Çm ?ÜÀì!ž\›‚¨‘¸p$mÇoËkü4Ðãómüò 3ŧ)šް|Þï,Û¼—£ç®’”iFoç†_­´lßþõ§sC‘ýnfSì~–ÌZÀ_›÷rüb<éÎÞ4lÕ…Ãg`sÏ’K‡VÌgβ­ì=~è¤LÌVv¸xúS»AsÚwëÃÀ{Ûèxí¤SS#X;oK6îáÈÙË$¤›ÐÙ8áîHƒæ¡tí}ý»ÔÅUÖ³BTB•*0LMMrš¹ªW÷'*ê2©©i–—SWF¿= —£ôÞ¼rPâÈaݸ<·&”¯Ã&óÀíî„ÐÒ8òû—|òë:D¦¡8Ð÷íé|5À·Ò6H”çåõZF*£>ŽÃé~_üÄ2?J£F¨?´·0y^¨hâñç“™²!•-sH´(¸¸Ùо¥/tq z‘›†Êú¹çwÆ‘)oySóT"Ÿ­KeO¬™ 3šTcÇŽØÞ¦rý¯Óé ÿŒdd˜)ùØ×ãàp'¢•Ø-_1rÜL&ÊjNçüámœ?²‹Sv!t]ý­ì³ù ž7‹Ã)j¡}L$Fd{ÔIv¬XÈ£¾æ§±mpÏ/ãI¦??‚w$`)z ¢N‘uŠðu‹X}e!+_n€`¾´”±ÃÞaÕeSÑ¡¹™ÉDŸ9Hô™ƒlüs']Í'Ës– !Ä¿K¥©Çggg¼§0::š¸¸¸‚ïj×®««kÁ¿#""pppÀÏϯà³+W®‘‘AíÚµ9uêx{{|ñâÅ‚W\F²³³±±±¹Ó?ëßIQJ´ú–½­3Í‡Ž§ùÌh¤mžÈ³ïþ…Öö)Þy©>ÖÉI¸·(¥—:ëß y‚I'ÜyrÎZÞ )y«3Ç…1ùµ7ù~—žaelS.–XÂæüÄ‹6qðBG?v¸Ÿ/ŽÑQú¨Î¤cWy~[´q`l@îÕ{n{4Ï-7âÛ̃·ûÚচ ß™ÀŒß/“dSƒé ®s-3ƒ§^e£‹3cò ž“BvZ6ë6'ðÆÏ&¬_ñ¥G~[™ÞŽOúÑñH<ÿf¡s?õÍOI¡šoÑ»GÊ©Xžú-•ÔžìI°ƒFdD ?­føUæu»`uü (G² ßÏç«2ðiíÊÝ­±WURm \íiqžà&õ±ùcY`¹Â¼×ÇâöÞÛ<×½öwøè¦“ÓõÒ d”¡+6-4 ú[ØÇñ£_žÉ¡ôÜ}#ÍZì’Å…ƒ9›lF³$>åe^õ_Ì/û¡C#fÙ7|U*è콩[ÇcÏ_!Ť¡Ø¶dÈ zy¥ 6Mú‚ÕA¡‚gõüÈI¾Ì…È$Œ*XÕy€ÇÚJP(„¨œ*M`˜™•»lŸ¯¯cÆŒaÁ‚ßÍŸ?Ÿþýûüûµ×^£E‹¼ûM›60oÞ<ž}öY}ôQÆŒSðý—_~Iff&?üðW¯Æ™•uýÀÐt•í³&óã¢-º˜„É·úíûóÂ+acSz`¥eF²sÅŸ,[·“=ÇÎq%1 ÍÖ¿z­é;d$£ÔǹЎæßsßÀ)œmþæÜGÌS™ºx{#bÈPœñ¯Â=OŽfô½uJ]f<óÂffÿ2ŸÛr.6“ÁšÚÐwðÓŒ¸¿>.¥v±(X¬É<»šï¾žÎ_aÄdYã^³1ÝÉØam©V¸ff cB—g˜«§Ý{3÷ñõÜä°ñÎŒXÑ‚/v|K›ý¿0qòR¶ŸºJ†Î…ê ÛÒÿÉx®O­"¿Éöž˜Ob§ûå¡k­¾y,§¤ÿIœnöæ?IüLXØ¿ð¦.Ù¾ÓWHÊÒ°qõ%¨A3Úw'‡¶Å¯ÈU C±Ä°sÆT¦.ÙÄó dé]ð¯Â}OŽfÔ=µK­Pež[Ï/Sf³lÇ1.&š°õ ¢E÷yáÅ¡´õ.y™ii§ùëç)Ì\Ωè4pªN㮃ûÊ@ld™ý2˜9¸a+±ºæLøä õ/ëLË üÛ·™¡–q.ZˆÝù3ã^û‰™T\n>Kj?˜¶Õt$]>Çñ=ÛØpµ ÷ç_Ç7µO"+¿ý™}ùA¡UMÿ÷òÅ P“ÃùtØsür"MMf˳Ù?ð Z[›9{ìdnÀ  dÄÌ¥¼Õ"÷U³®p`Ã*VF5àücY"9z"¥`½Îs“7|Jϼ“ì¸ãlY¹’£¾ƒiPijVBQT¥¹}å÷ÚÚÚ’““C—.]x饗X±bW¯^å¹çž`„ ¬^½šW_}•/¿ü’W_}&L˜P–Éd*’~NN999ØÚÚ9^©,—X4v8o®»ŠYSÐYÛã¤%plÍOŒÚµ›N”R–Iß5™—'üEœ¦`pñ¥z ƸH.ì[Ãû·3‡#ë•h7ŸÞÆo,`éòK˜õ¶89ÐR9` S…q$y¿=V“kƒTâ¶|ÆScçp4]E-º¬8Îì^Î÷ákùkãÌúj%Îý¥?ñÈvö§pñpÆ)'‘¸ˆ0|º—-û?â÷ïPãGiæs¬ÿâE&.ÞMãVtèÕSÌIÂ÷­â»ýÛØ>v³G7½µVm-™Mï 幑è}šÐ¡û}x()DG]àäÞ•ü–Ì#µ-²‹B:›ß{Œ;-4iIÏÆ¶/bûî5|?6Œ£i¿óëà€BUä°¯yâùéÉr 0¤#ý»ØÎöy|Ó^&Îû†A… LK çóa/ðÓ±L Þ hÛ§î–D"vüÀðáô¨¦/í-Z¸øOvïÞCÔåË8;;ѶM(ƒˆ³³ó-¦¬ ÜöÚ¼‰¤Ä44+ü¼ÊJ\#yÛ—¼>;‹~·gÕ‚ÓÅ¾Ïæøocxzâ^ìúLà× û0â¦sd>4ƒ‰K¯Psøo,z«5Ž À †lƨ‡ÞáëÓgæPªß¦²p÷2ঀb«ÃQQðõ°Â`£ÇÈÎÑÐEÑÓ­ŸÝŠí¯Ø[ãgû2,¤k 'Õ9èqÇÂöIlqs¡¯ w·gHǛϯš”ÉŽX Ÿ64P4² ÝvkÔw æ†$ÂΚ° î_Ö4̵Üx©¡…·Ä!”7§|HÊèXr&3·§KË!fß">±”›àå ¯0´…;·s0©–¸•;Ó †[ê¼îçÃož§G^˧Cp+ª·¢÷­î“¼å[Óò‚5›¶O3®§oAÅFçÂè™÷ê24°\Þɶ3Z7P°³³½vŸU/³nÖbî x˜žVèìühußZùUvØÛše™Ƽ9a4|²-~6`ãÕÞO6,’?!„¨l*M`˜Èå÷  oß¾$&&rúôi¦OŸÎ°aÃhݺ5ééélܸ‘?þ{{{:uêDëÖ­ÉÌ̼îqòÓ/8^£rõÏOø`ÝUÌŠ#ÍŸùœIc»RÃN##*Œé^ç›íi¨¥Tíº<Ãø1Áºô£gSl 'ŠeãŸä]æÀôÙlþÝŠBÑÒv²pu î{ÿ÷Ps¼m42/mdâèW™}"™Óæ³ÿ‘7É §Æ®àíWçp4ÜÛ¾ÀW?M—š(Æöü1‘×>]ËÅUïñ¿ Yø|Ýb'JÊÁÄôý?þzMÝ­ ç*Û|'ï!úïOøpy¦ô¼µàÅr‰5K¬:eïw÷Î˃JbØ× v:ûx—©ÝòrÛ?EµäMÌ^‰ø3þ|›v…ÖbWÓ£ˆHr¥V±Ú–s˜ G{3qÙg<”ÿ‡°på¯q |m-Û~ùƒ#£YÞ~Zê6&¾>ƒ#Ù<:e:öðÉû-™™<‚G¿ÝÀ‡-£ãOà«Èfï÷ï0íxN!/1{ÚHš9æ•dö%¿6œ×We¡Ýá… RSSynÔ‹„í/òùßë60}Æ,~þñ{5lpóÐY£]ÙÈ×ßNfáŽâLvøÖkÇ€çÆ2¦o…X#·1oÖÖ…Ÿä|d4 &;|궦ÿÈ×Û§6h$nÿ‘7¿ZÈöc9hê*žo¸ Ð8bëß¼6·U‹ßÀo/Æ2ð{ƶ\IJÅ3f F³Üÿú(F=Ùõµ×ÿj2‡þœÎ´…ëÙ}*šTÕOÿ Ú>ù.Ÿ ââîp"©Å¨ZàXè‚°ª>€žÎ¦_W²æò£Œ¸á‚­!w4‚¢(胕’ÿŠ’»@K>KZ 7&±üT6R-ÕÜï-–’ËQëýÝøàÞlÆÿÀ˜¯°s²¡ym;Ú5t¢_c[Jéô.5ÕL¬ Ñ;.ÓfGéÛx¥˜Q¹¢…:µìp“®ß2Cí|¹¤1]¾û„Ïçí"*¿›L3“ph1ï ÙÎÆ7¦ðÃS ¹]3ë-gŽs2'ÿdTpnß‹NÎ×ÿcÞÔ>G9^hCÚAæ|[äüV£±V P¯pñ²¨ß­3þ3æpÉh9œ_þ®ŸFȽƒyâñ‡éÓÄ£h#­¾:]ºÖå«C'ÈÑ@³\eãOÓõ·Fô~xO ¹—Ðj2„TQ¹UšÆXÕRö‹†Š‹‹ S¦L)èºvíJ¯^½˜0a½zõ¢k×®·çxêUV/ÙEš¦`Ýä9¾|­5ì@‡CõöŒþì%:Ø•ñ0³ªËƒ/=Í}Íò‚BCuî~5õ ¦Dp*º´5Öuø |›‰C›ãm `_£¯é‹,1Ç9—Ÿ_ ç–Ìfc²ŠâÔ™·¾C·šèÅÖ‡Ð'>á³!è5#‡fÍfg)/ÖVº2îýGrƒBƒ/_üˆšPÔ4¶.YGÌ-¿—BÁ©Ç(^ëæ](0ÕáÞv WGgŠ`ÉÒ#ÜÒ LF²- G_,ÐÒ9V§^€cÉàV1Ðâ™q<Tø¯ÇïÞǸ×W9êÇ“ò+"ñkç²,Zãß+¼UØÓäéQÜï©ÛÿdMt^e‡³hy$f]-†½õ̵ À¦¼þ4­mî|mø‘¡O¶;'GGž>Œùsfòo¿Iƒúõˆº|™! çØñ7¾¢äëg>"Ì­7/¾÷ _¼õ¡j?ŽÆ‹ #)22ñjËvgS»û#¼ôÞç|ûîpZeïâ§—_àÛƒ9€‚}í®Œxó}žii…bÛ–W~›É‚¹3øvhk…z•¥ï½ÏJ»!|>¾Îjig‚c‹'yûé–¸ë@Óʞ˄–Èæ÷gð›3Ù§´`ð¨ÿñú¨AtvÄÍÝ =‘ž :'\ŠÿÍôÔDo>ËÉ3·ñ½ å=5LF&O»ÌÇ»sðjåÉ7#øãå,ëMÏÒ¦=*:šuñcùÿ2s˜ÃY“™Êwó"é?9ž7ŸeP#Ô—Ùc˜[Ê“»Ø–ÚBéh¯«<¨9ž6ýÇÿÊÆõ³™øLg‚ìu׿—šcØã½>Õ¹v‹Öȉ;ÊŠ)oóH÷Þ zw!‡“eÕ#!DåUiz ¯[û'g:Áá“&4ô4èÕ â‡WGº7¶bÛžë…4Œ)‰$¥ÉQ5ÔtrE-›RG°*´éZ¢5×¶F ¾ $k¤§ç·§²OMÁЪ7=¼Š×$íh}OW|çÎârÂvž¶Ð¹YÑaÕ ”×bûéüéÜ¥6Ÿí?éÄ1NZÈë»YVÔiAÉa[Ztk‹ûì…D;NœÚ‚j7yų÷µsa×Ö9Œ’ÌsÏgpx\oõ ]¡!þ%+¤:O|¼ˆË =S#·†žÃÁÝɆÎ]ÛPbNÛ†4 ¶bÁÎ3?c–‹G9’¬¢÷ëH·%3¢«J»Z:vy-\ü''NžÂÉÑ‘U+–·Êo»¶¡<üÐ<ûüv‡ïaúo³øêóOoêZNrˆè¹¿ò\ý¼›m]†~¿”6ëfñÝ乬>žPðZÍÇÞ¹ïòèþ‹ü6¡ŽÒÝ-„¨|*M`¨(J™ÁÚ„ °X,L›6­`ŽàìÙ³Y·n[·neÚ´i\¸paÆUèx¥ÑÒ“H0j Xáà_²¢¤óÄÏ×¥D_—FÆÙ¿™6em9ÌÅ”œ’Ë©ëËxÈé<ñó)Y±TôzôJnÚE£Æs5΂†‚›5J{6éªùã¯ƒËæX®ÄX Ø¯Ð¹yàV22ÂÛÇ '0g&’häÖ꺊O÷R{ô¾þøê ))D ªÝì1tÕyô»è>û¯/ã³1ËøÆ³!=“Ãî'Ô·”U^tîx{”ö·Ï[PHÓ®‡j2W¯f¡i&Ö¼BàËeeD)ÔÄ8Õkƒ’Ç÷Ä×S·)0Ü^â\^¼d)/Sæsvvæ ãé×ÿAÖ­Û@Øî=%ÒlØ Þç êêлo= /»¢8†0èž@æNÝͶ“f:·(ûöcå_+£É©åªø™ÎÌæõÏ÷4zc›Ûqk!!€™ãë7IuF<Õ«” @Á³ç zû*Ë?}‡fNc¹¿±ÄG°iî×LZ‘„¦XacóÏ÷{i9*™šBug}‘›|äþTv™M£p§ä±Dæ]´fp_'‚ ²«P£¾=µuéĘK–§^ÉM'»¬Q÷€ÎÅžN~ ûO¦°.Åþ…Öùɼ˜Ä— GW7:¸–†¸ý¬¼CõÝû\ìù< bT@#ûÜ.ZÀ½Äe©‘““»çµ;‰…¨ —)«/ÜÊÙävÎiÄ\ŽÁBýëV8nf½›;® \Õô^Ȫÿ5¨@ÅF[£þ¼ñÓýŒ¹¸“Å3eê°‚á¶jÊ~¦ü¼™¡ßô¦ ÙGq vïQLêýÑ{V2ó—éÌÙtžtU42OÎeÒ²aÌê#=ÞBˆJ§Ò†:½‹ùÚchÏž=‹É Ú·oO‹-˜9s&çÏŸ§W¯^têÔ‰ .pöìYfΜÉÃ? ÀªU«HHH(’^£FНTÚµàMWFó¹¾ø¸EÀtú7žzôKÂS5쪷aðã]iè…“­¢ÿ泉«ˆ,ó×[c(w¦ÿêªWËDÕÒlj^‹Ë•òµæ^÷Ðj™/Ë_•®ü#ç4‹¥è»¨ò(Ž xäÃy s5‹±àÕ¬™:Õ³¦ÑoÂ÷|ùHÝ¢ïDSôX[•û¨¹Á½â@£~ÒÙ¿¬ý¬ ®e•¿K.]YU+¬nÓU¹pñŸ¼öÆ[e~ß°Aésó禦¥ñècO”øþ¡Þ¸'Qï‰.eÕkú£SÃ‰Ž±û1FmgÞŒEü~‚ 1ɤͨš…œp/ÏHìãüøÆ÷œlü2KFÔçö,êjærT,ª>”ºe÷“)}øøçx”ñßóÁã«yWË].¿vß1¼ý¬c¾‰Æ·Ì…rî+?{Zؤ²}_«ë¹ÓÒÆÂñCÏ4ЫZKãŒìŽ5ÓÍÝ gkpRsX»%‰=ifžnf‹Ÿ-dgä¶+‘Ýz^lZ²·ÐÝ×O%‹µk⨙b‡—¢’šf&ÚÞ‘—ÛØän¯3ðÈ}®¬ú%‰¦]%º»Íàjd:ó·¦rÎÅûîùÇ‹§jPãÙû÷ìC:Ò°´aŠúB7rE¯+xvXÙÚäþ.œ>KÕ®Ý+³³~{eÍ(ÐÕ¥–U•”íkØšÒ™^.Åî‘Z6F“ ¶†›ÛÇ*¸ mp5KTÎoݹ\á{¨‚CÍ<ñN=ò+OþаŒÜ4ÓÏžå²ꕸ ØR-ä!Þ éÏc‹^á·Ö¯š‰sg.¡"¡¢ò©4¡µµ5³MÓhÚ´)ñññìÛ·Èí1ìСCÁ¶Ô­[· ‡pذaÌœ9“ˆˆôz=íÚµãÌ™3ûøøøÐ´iÓ‚Þ këÒ#1ÅÑ 7ƒ9b¢ãP (zó×2IJÊ.pe³ë·_Ù›ª¢÷éÏ·K&Ò§Ðxó‰Óüp+…S˜Î‹j>V(DzIŽºLšF‰¡Ajt—Õ¼m½K>º,q1ÄZ(6LVåêåhT@çàŽÇ-¯À¯‡JOó•(®¨ ¸y,>¡Ü(MI"å:_Ûø4gÀ Í0rG–|Åk.aåïÐ4tÏ\^:¼½lPP©ÑóyÞ¸Ïñ†»(®¹-ÜñqW‰-­7TK!é6ÍQiÛ&”^=»“ššVäóãÇO–žNjZj©û¥¦^û¼MhH‰ï=ø@¹Ž_ZL§å½3,Ÿùì\F<ò »:0â…7×8ºôu¼ùðל»áQ2Ù7é-&Ÿ fÔÔ.Ø\½LTÞ7iIFÀBzÜ¢.ÛàäåƒK¹£ÆÜža Ý Aœ›>Τ•ð~äy.§h8V«IM;ßÅЀàë–wŠâäÄÿ=–Ã+SxRØYÓ¬©ïqÂþ¨…ƒKÓù`rQ#]CÁµ‰7¿<žÄÛRùbN‰9`°³¢v#Fºó oÉB0Ôrç“>f¾Ø•ƤE©Xtx¸hZô:p òdúHk¦lHaÑÒh¦dƒ£« ­[ûðNwgVÑ×ÅÞqZ<['½È÷ç]¨×©7}»„ÒªAM|\lQSγmÖ×,ŽÉolÕáØ°qÁb\z_?|upΠ·b_v¯ÎØÎþ(qÇXñݻ̽TöÜY_{º[±ï˜9wÍ—¸å¼õb5Ìoél}Sû(î]ØÅ…Ík’QÑ0ÊKïUgÒ÷ì”ÿdQÉŠ=EØÆuìÒ÷åõ‡ƒ±ÂÌ¡éïñ§Úž{ºw e-—‚0¶žÕð°Q ïµн}îê½–s,øpqM{Ó»s+‚=mòîdV¸úz¬ð `g/ƒ¦…•S¥ mmm1f1¼öÚk¼öÚkenûÑG•øløðáÿÿý÷ß—¹oVÞû󇤖`¨G³zV,ßgæØÆmDZdH –ζ#ņ‘jé\¾’»¤¶Mó.t*©idœ=“¨•™­òSœmßæ}äì_Çú¸\$ø3²oí®Z@çÑŠ6õJž–S;Øû4A…'÷YαaÓy,(Ø4lB)»U™“»÷‘ôt ŠŽÜ4²SɪžMSÐÙbï€=—šBj‰`×Â¥CG‰³”£‡QïF“‡ßa‘í<1ÿGN—ˆ€+À†æ¡M°]¹›ÝëwÔ¯Ï WQ´ lL#g§.†±ó‚…fµ‹[K=̾3·´äN€êþLûir‰Ï¿ùî¾û~23~›MŸ^=K|ÿëŒY4¨_ßçͺ¹ƒ[.séŠ n…ŸJä…HT~¾zÀÂÑE³Ù•æÇã?LâB/…V£Ë¹ºŸåÖŸ!;÷õáÛR6YøRêk0bþJ&´,ï‰k¯ŸzËEÎ\T¡éÎkÜ‚q ÈûgúV–oŒÇ¦U7:Ü`eEBÉaåÅÙ:ñý'…f±:»ð˧…Æeúº³h¢{‘]¼ë{ðC}’iµòeY«âê¨Õă/š”²}Y=!Ý«ñG÷oêRÃ…ñO¹0¾ÉZ×óaûç>åÏGt·:ªá?B3%rrãNn,±DoÅ&˜ÇŸêZ0OÚª~WºúÏà\^ð§eá—‘÷ðKyª¯Ãç{3{ì*bU@³·óGF ø±èv:7ßÊ>Š;÷¾ú‹Â>bk² Z'¼Nß??¥zMoùöî2<ª3aãø}f"3q#‚ .ÅÝ‹;õÂÖ}Ûí[Ù¶ÛÒ­n·²êîN‹wwww‹»Íœ÷CH @ŒÒpþ¿ëâCgÎyÎs¤pîy,KɱÇt,.]9¦]Õné¤ÇrÏHÉûê«ï~Õ¯ØäXUQ5Âà‘¦£»véHr^[¨á©]:¨ªM’+KGWþ¢·¿ùQožò¯RC5+É;;F{vT|~N¶WU×®gÏö C…ééàãÌýîøñ—ô8ùåçï¶HõÖV>†©ôãõèøe:š‘;¶ óä:}õÔ«ú#þ¬6†Ÿ*WÎÔµw›v˜`&ûè½üÎ<¥”O#‘$›ª »Uƒ+Ûe&/Ò~Wó¥åvùÉŽÕ†žÖ?¿;$—á­ènT—"NÓÌX©·žüDKfÈ”d¦Ò¬ÿþKnÍ‘i T½WYP IDAT^æ—.Si ?Ôg/0Ó­øãõÒ/Gåön¨QßþÇÕ#ª¡¢} ¹¶ÍÒ´½…ƒ“;f¾Þý~˹£:ÓöhÕúãg1ΗuH[w%É´UVdDYZslŠ8Vƒ«ŠŸö²ýr“ÎúÝÌ<¥­ö+9¿Îö9°Šl9[ôÉË?hGÁYa]'5÷4§ü†"9\þ~~Z¾b¥î¼çþB-„ù¡QʃXj®ýš>iƒ .c&¯ÐÏ”Y©ƒº4ðäVZZ†L#Dá[èÝ:±x¡¶'Ûjjø¸·õÑûãÏùó¿±Mäa U¯ÿ{K½;N£Ï^›ä‚<Ô¤{g…ë ~ÿj^n±âÊ9¦é/þG¿žŒÔ¨;^t’&OÌ%Áu½@õ‚ÛøÖ×u¯¾­‡šhºõn¥{ÿ5JuÎ7C²-H~R×W;ßÿS†ÂúÓ»¶S˜ýÂ7¢@gÖRì#yÔºNã?ù?õŒôÊûÜ”;3Nwn×Ö{u(6]E ‘=Ãt+3á°¶o\«•k·ëp²+·ÇaWH»¿ëÅ[êŸÓÚ4³•tt6­[£Õ›(>›ŸšÞúœþÞ†fpS…ùQËÛÛ;·Õ0oqúóuõ,‹ü¥.‡¼½Ï÷»MUG?©'çŒÕ3 b´ôíÛÔù_ø¸•š”.wXW]ÓÇÔO3¬½Ú¡º“?ÓÎÝŸëæ¡[Ô³U5y$îÖŠE•Ö¨»Ú…Ï×òØó²„Œ zæwjïÝjíò÷tKÏä ð•-#E©™.™†—ª_ý/½}wã"Æcy¨þÕýåXò–nìöŽœ¾2Ò’•–í–ix(²ÿ“zj`hÙ`·…ªM—@M¿–4k£–µeÆlײå;ë T»Gþ­ÛêøçØ¿‡n½¡žf´A¯ù›ö_ÛGMÃìJ>ºIs~Ÿ©½áulãén„’d&.×ë׿¬Ma ÔªyU¯ä+[Æ)íZ¹D«f©R¯;tC“²uó3»ê_¯ß¥=w Ù/^¯N_4V«F‘ òÈQJìaíØ¼S'£Ðô_ï’¿]’êðп5vÕú|þ‹Úg¢:¶©­`3AûÖ¯Ö&w{ 霠ߖ–©ZT½Z¤^{õeÝuÏš9kŽšÍj§%%ŸérzËØ›‹lM,.[P-ÙþxH7Ä_£amkÈ?ã ýüµ& V¯—îPG§$y¨qçv ý~ª¾{ýkEßÛUµ<âµmÞwzû§ý ò1”y±þª×±§êó…©Ää_d3âT£uOõmSò¿êœîÖ¿-ÒƒÕÈØáÕ½¾Bl©:¾k£öÕ¼KoÞÖHrëÈâ ZpÊGžÙJ8¼E 'MÔœ=^êøèûz¼ãÅW†sÚó'Ü@yrþù=xÿZìѺï‹=eºf/]«Í;÷ëpL’Ò²Lyú…ªjT´®êÜW×\_ÔD\†Âz>£ ?·Òþ¤+·ëp|¶¼‚ª¨^ËÎtÃ-º©s¨í_?Ž/z¬¡ 6÷}ª]¦ê«o'kÎÊ­Úw,Ain›œáªÑ¨µú »QëäY¶}d( Å­útZO-øå'M˜µLkwÔ‰Ät¹ où‡«zýfêÐýj–¿æ©‡šù·þ4[s­Ö–'Ÿ’)·á%ŸÊŠŠn©n®Õ˜á-Ϭái¯©‘ÿú·ìÓçiÁòÍÚs,N)9’‡+WWt‹ê7úF]Ó)R¬f ¢ª0ÁP’ü•‘‘¡Ã‡(*ªV¹—øð‘Óǹ ÚºñÝïUõóñú`Âbm:” t³Š÷¦»]í7?¡)³ŽÚŧõ?ôÕ'ÁzõŸ5oÓJM9°AAÕ¨Óoëÿnï ÝÏöÖÊßËëL ´ù»¾Ÿx•¾þè+ý¾`½vO•Û7LõZ´QßÑ·è¶Á ÷òËg¯©Þw¿¤{è¤7ßøBSWîÑ)éJõš©÷5wéï7·S™Ùò™Ùª~ýGzyäzýãÉZøÇq¥ÙU­ù@¹í>ÝѧÖYÿ¸:tÕßéëÊãõîÏ 4ýã×ô«|ÕTn|M¯Þ ¯ߢo ìa é¤[îªoç®Ö–ÅhYZŽlÎ`EÖk§1coÒ]×·W•2Ÿ‹¡€6èûÉmôÝ'ßhâÂõZ;w³ÒM/ù…E*ºûͺ}h_U-p#¨“žþî5zï=}=cµ–NÛ*ÿššv»[Ÿ=2FQÓoÕ¤¥§ÊZ± ººOoM4AϽð²V¬\u:FV­ªgž~¢L¡P2äl¯¾Ü¡/^¯žß¥˜EDwÔýï<¤{ûæÏæk(°÷Súdœ—^úüCÝ=â5ɯ²¢;Ôã_=¢Ï Ðåp®¥f«¬¯|§Ðæé“ óõùk?+ÉåTX­¦Ð)8¯»…©¸õ?ë¿ïoQRއüª)ºõh½ôâ-Ù2´XÁú{H ˜Ù¥ã_¡þu»4<ëßß«ßßJ³·M†èñ·†èñólÑû¿Kµ÷¿*îÐfCôfCôb·4ûH†O-u󘺟;_VQ[+ nÝüpÝüpqàTÍŽ#õ`Ç‘z°õ€ŠÄ0K°@à†íûÔ<:êRÖç¢Nœ<©´Ô4*$$¸ÜÊ‹‹Wbb¢||}T9<¼ÜÊÅÙ²4÷ñ®ºmB¦¾³Bãû•Ï’(›eËWªq£è‹/CK"&KJ"–›O)Œ¿ZSÖ¬VaÆæ •ÝîÄÄD%''_|‡bHNNVbb¢ìv……–`òà Ñ¡}[Báeæ%]†å¯HÞ6B!¥Qá^Eìv»*……É0 ÅÄÄ*..¾LåÅÅÅ+&&V†a¨RXX‘kÀ¥VÙA8,+o[îu%W!_CœN§*W?Ýr¸oßþÓÇWvv¶öíÛº¥°råp9Ï7)\b†éÌ퉒 ð̽~ÌF @©TØáùN§S‘U«*&6Vi©i§'މˆ¨,‡Ã!£ˆU©MÓTFFF¡%/||}r»§ÒRà/ ÌK ô’s¤t—”mcC ²¹KR8í¹ÍxVÈŸ9øë¨p“Ï%55UIIÉÊÈȸøÆy‡üåë{ñéä௬¬Y­Â¶äëëKÀ€R¢ó XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Åy\î \IÜ'æë?ÿ™¡Œž÷éÙÁÕJ‘ºs´éÛ5~M }áoêèc\‚Z–ûÔr½óÖlev»Eô‰ä×…+Äyï«™¡ËþÐ/s7kol† gˆZ ¿C·´ Ô_ó @IU¬`è>©?^S¿r]x;ÃS­oyVw¶(Áé™ Ú²p‡¼ZµU=^w%ÉLÙ§Åk²Ô¨k…¼$f޲³³••ã–yÙjgE¦b}¬q¿ìShïûôÌàj²—¦”ÝWSé['ëÝŸÖJõºêÚþUdOM•”¹‡ÂóÖ —\Å ††Ÿõ(¯äü×V·Nm˜«ùûjÖ»“øå¿MÚU9²díXfì&M™¼VMê·!J’L¥m[¨ŸçëþÎ Z ØÂ;ë‘ÿt’ ®ÓŸÊ}BË–îWŽM:±j¥v\]M¼JZHIï«[û6íP¢QS×\ßO]C.Õ=?½péU°`è£ZWuR­Ó¸´#f‰ì÷W½vÔ»Ri;5ºtjÓVtIMÊ¥¢Wwª6mا,ý=¡ðO—½o•–óR³N upÉ&-ÚÜO [ù”¬å®Ä÷Õ¥””tÉ à€KxÏ/V/\R+–”™¦+çéÅ[´ëX¢2íN…V­«6]º«OË9 Iîãšÿé—úusœ²MiâËOh¢$ÙkëúgîT`CÙ±;´pÁj­ßsT'b”âòR`Dmµí3@ƒš‡Ê³UsÅïÔÌ)s´dÛÅ¥ò«\Gí®î§ºžgÝ3Qóß}Eß»J>7R ¶¤äu­”ÕUO<ÑO5m’\ûõÓ kËU÷êén™šóëTÍÛv\Iž­tϸ‘jâ:¡u‹–jùÖ:r*^ñ©n9Cªªa»^Ù»¾‚m’·A_}8AËŽgÈm.ÑkÿX"I²U饧ï«ÈSçKYœë-IÊц¯Ÿ×‡1½ô̘­Ÿ6OK·VLšä Tãö}4²g2x±€ mYº^±^µ5¸w+nZ¯eK7*¾e{nÄË»¶q}õü½txæ$M\¶[ÇÓƒÕ{l%Oý½˜÷5RiÛçè«)+µíK¦¹A<¼A’Má=ïÖ¿‡Õ”-ãâÏÒifšö¯\ Y˶hDZ¥»½RIõ»טÇõÍž·êJÒŽ…³5}Åí;™¤ ·§|C*©fÝê7¸³êûñ#@Y]¹ÁÐLÓÖ ê½…ñ mÒNý;FÈÏ¢ƒ›VhÆ—ïiã‘[ôð (9E÷¥ë|~Õ׫ u¸n„Ú‡’Í¡Êù]JvkåîlÕhÒ^mƒä›sJëæÏ×ô/¾ùàƒQ«d—ÑLÞªoÞþFK“CÔ¼[ ¬âTVÜ~­úý­1²ËáäÝJ<¾^?¿»Rk=¨u·FòuDªª‡¤œm^{TözMÕ³m˜‚½3tpÍÍšú•b=Ô£=+Éð­®®£FÉùÛ÷š“ØP׌é¤j6ÉðVøùÞÁ‹}½Ïìâ:¶RŽÏGTuÔZ¾f’v¯X¬%“¿PŒí=Ò3œ‰mò˜I›µhSªœ [¨YP”ÂZiÑ•Z~¼­T9÷*¹“kþ÷‹µ`oZ¶îªVÞ~ª[½†¼‹}_ yWn¨>C«+rê—úãP”†ÜÑKuí’WpåÜûb^üYÊÝ.U›ù@ï/Ž“_íêܧ½üÌT†‡j·n¡Ê ækéòƒê;¼Ö9ÿ#›§Öi¾OO=ôxÕ)˜Æƒ<Š}_=ƒ#U/8Kñ~†dóSÕºµÕ àŠó,Rö¾yúaIŒ|[^§'Æ6WÐ9YÎçüÏ›™ =»OÈU©«®Õ-·U<ÏÀb_?\ÌúÚíÖ¡M[uJ¡jÛ9Z…zšþjÑ¥¹BÍ“Z»áx‘!§8l!¡ µ›JKM/Yî8mÛ+Ó·‘º¶,<Ý¿G•–jS½|n‰ál¬¡ƒ…çã¢ð`Cîô4¥•jšÑR^o[%]ÕºæéP(IrTSíªv™É‰JbÊÓ\®£ZºüÜÁMÔ¾~îl3¶ÈjièÔšUÚšYÄ>¶ uÒ³p(ü3œó,¹thãVÅ(Díº7)"^„á§j‘Ò©UúeêFN-íÿ±¸+´ÅÐ¥¸ØD™¶:ªRéÜ7Q[x¸Âm¦vÅÄÉ¥‹·öeÇîÔÂù+µ~÷QHLSF¶K¦éVvŽä_ÒÌ$Å&ºe WøÙWßðW¥POKVdQl•"UÍqî¹›'´nÑ-ßr@‡c’•–™-—iÊ•ã–*›¥\~¢”×Û¤°sf¹´Én“L·»Ô¡ýJ“µk¥–Ÿ”[G) !V'%IvEÕ —1o³o详mý OBc Wª—vjÏâ=KnÅÆ%å>á¥ùÑÃCõýMsMЄyßéùùŠjÚR;¶Sûú!*ñ¤¬(Ò %w^Â)2èä}h+ÆÌšîK5þÍIÚå]_½ûÖÐê!òsxÈ–±Y_¿1MÇK\³Üƒ6£ˆ‰= Ùì%yyvË-¨“Z¶l¿Š7ŸmIîëjVìgÉ®ë+H±Z¹`ëÆž¯^¦ÎùÍÂæ§:Mk+Øp)=ýìRPWh‹¡d¯ÖM7ôÜ¡·gÿ¨WÓ¨KÓ*òWŠnX®…Û2U½× ê[-?ìÙR»–BµHKùEÝë+ؔš4Wµå¿xƒN^¢È¾Ñ ·§ê𖥚ºì”|½Œb¾Œª™ju¿ZmÖ|¯•ß}$×ávjáPNÂ!­[¶]‰Á>2 MKê¡z­+hÍjýñítù÷o®H—mÑÌi•^‚éú=kÕS]Ç&í˜÷‡…tVƒ S 6kÖÌ Jñ?+²EÕ­*m[4áëYJi&»ËOÑmë¹yÉ®7Š%ç ¯8&³Rõhì,²UÌÝQ]ª¬Ñ”µ+´i`”Z]¬õ¸„÷õ|Jò,yÕï©Ñ­vè“Õ?è•”ÖêØ(B~F¦ŽÒ‰°º½g¤lç«W›JÚôù—ÚàSOõª†),È!wò mZ²L'½k©GÃZ ÊÁ e8ToÐz,b¦/Ù¨–*ÍôVHdmõ¼©—ú·®RhM=¨>ºëštý0k³¦ü¸Qž 4¢^sÕm:T÷öÔ/óæêƒ×ÿŠlÐZÃï ÄŸ^ÓÄÒT- ™ÆþÝKSæjé’iZŸi( ¢®: »CcÓ&é_?ÚZ>ëï7zkÂÜuúö½Eʶ;V­®ÚŒ¼KÝ7¤÷ó¸ÁmtËíiúiòrMx­2ìN…U‹V·ëîÔð½_é¥Õ·¶©J÷kuKÒïš´z¾ßj—Íîª~¾QÂë‹ËؾR+cìª3¼ÝÙ½sϰE¨K׺šõÃV-^Ÿ¬–|.Rj ïëy”èY2uÕ÷ʯÖ<Í^±Ms&¯TšÛS•ª«uß¼`w¾zÕTTt5mXºQ³×&)-Gòô RdívscOu¨D4@y0LóœŽZçµaû>5Žº”õ”PY³?·€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`wEC÷Éiº¿cÕªÛPµê6TÝok«ërת°ÌãëôÛûÏéî뇩sÛÖjР‰ê6i£V݇éÚû_ÔG3w+Ù}±RL%müZwuo®¨¼smpÏ¥þ'€ËγBoŽé¢ºõrï}íöÏhQöå®*2Ë]rã>¦ßžyQœü‹%Á|îšûÚãzêË•:–iþΕ¢¸Ã;´âð­˜ñƒ>íô>«šûE”®¿þ[÷=;I»ÒÍ"¾Ç•ËT↯ôȃ¯köálq÷P^®C—ü8N/ÌÕEÛ.[ *9Ó”˜u‘×y3G'–üO½³^Yg—uHÓþ}³F=1‘Ph9iÚñó?5â¦W4‹P€rvEܽßé‰W—(þ/› %É¡¦w¿¤´öW@nºõ©7ôÃäZ¶t‘–ÎøFoü­¹òï†éÒ¡ µ³@ãgÎÉÅzuìuºÿ›-JúKŸ'Ê]æAMw“F=5I{øA—@ÅïJš½KŸ>ù––%¹e ¾Jm+oÐòí9çÙØ¥ ¯ ÖÈö)Gjþè$M¸3L»¦|¡š©%[)&Ý®ÀÈújÛ{”î¾{¸šÙ”sj~úôký6w•¶N”˪ÚkÐßîÖ½jÊQܺzÖÕmÍÔ5>A ´ø<ÊþòG­.ÆØ²´ õÃ7¿iú’ Úu(VÉY’—_°"jÖU³6ÕgÐ õi&¯âžþ<îCúáþôÔüX¹LI†‡ªö¼YŽ|­_Ïûœ%SÁƒa¦6ø/½¹&U¦áTËÛnQ—Ùiy±öuiDzyš?Y}º]ibb÷¯×´O6hî‚úä¦úéî'4%/”I’²ŽkDzߵcÅ<-ò}ñ·ºò,fmþA ,ꋜ4¥îfj““fª“vݯ¶¦|¶SYŽ:ñï·ô\çUºë«‹Í­SsŸ× þxN×ÓœÄÚ»ñ„ön\¢‰¿¬Ó[sÞÔ ¢Æ4â²²Eª×è.zwáD2ýÔìæ—õþ£Õõëè¯/wÍp©ÐÁ0mýûzìýMJ7 9›ß©ÇVÓŒ©Åíjg*sÉkz`‰)³È]LeîúZc†Jr›Eér'jÙø÷4cØëTÚPåÎPìž5šüÁ+šx*·¨-¨º¯‡üò·1üÕþ‘75.ùe\ûOÝÒÜ_æá%ºØpEe¯×/ü|&ž ®QO5ƒÜJ¾¹‹š¶l›û§õX}²ÿÌ"®=Ÿitë¶jÚþqMK/¢8Ã!ÇE&]ñô*öŒ£%eØ<ä Wí½tË ïé©n¾yÎTΡ)úeyÙ[ =ªõ×+&é«'GªM„wl*'a«&¼|›Fýk®bh<,«‚CSéié§»mš®L¥&'+ùôŸ4e¹Î$œ3ß§Ÿg¢CÆ_e2N#H5ªœ¹!î4?ž¨rYËÞ// IDAT©.·¾ ŸçÍÑÄ7Ðà†A²Ÿz˜­¿×w»h6¬ª‚à ̡Œ¬ |ï: ÕëbÎAÃSAA~å{ƒýÕ·Kk5ŽRd¨Ÿ;éyj†^y}‡j\Ý[=ÚG«Š3/fÚýT%Â÷L·RÃ)'¿VUÁ‚aÅ–·]ó¾Þ®Y__dCÃ[ ÆÞ¯a•ËÖÌœ­Ÿø¾>øå}v_…׬¡ª!>rÇï׎½±§»×Ú‚:ª{sÀªH1†=X-Ǽ ñÿhQ®‹Î›®TØ»M'öžuï2•S²`èôVZZ†Ün‚!\nn·©´´ ù8ÿÄ`à磔ô eçä”é €²ËÎÉQJz†ü|ÊTN‰‚¡ÃËC6æäÔ ¹\î2Pz.—[É©²69¼<ÊTV‰‚¡a òSjz†Ò3³èR —Ûm*=3K©é ò“ae*¯DÁÐf³ÉÓn“ŸCq IJËȤåþD.—[i™ŠKH’ŸCžv›l¶²-8a˜%œb4;;[n·[Éi™JI˯Ó!_‡<=Þ¹ xžže*¿ÄÁP’233s+år+.!EnÓ-?§C>>ùù8ÊT!@a)iJKËPJzî˜Â ¿¼–BCÞÞe›‘T*e04MSYY¹c ív›2³]JJISZz¦Ò22Ë\)À>où8½àç#oO»\.·l6C^^^åÒk³TÁ0_vv¶rr\e® ø<<ìeî>ZP™‚! â+ÛÔ5€ `G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8²`š¦SÒ•”œªôÌ,edf•G½yÞ^rz{)ÀßW~N†Q®å¦iš¥Ý9>)Uq Ér›¦|¼½äpzËÇá]žõËKËÈTFz¦Ò2³d3 …ù+8À·ÜÊ/U04MS'b•šž!§··|œÞò´ÛeØŒrO®`u¦iÊt›Êv¹”–ž©ôÌLù:ªX.¬TÁðxL‚ÒÓ3à'‡§§lv†*ÀŸÁír+#;[II)r:Š *s™%NtñI©JÍ…^^„BøÙì69¼¼à§Ôô Å'¥–½Ì’llš¦â’åôöÎm)´Ñmþl6›!‡§§œÞÞŠKHV¦ŽÉ-¯$'¦¤çN4ãô¦¥.#›Ý&§·Üy+E”©¬’lœ”œ*o/yÚíe:( ì<ívùx{))¹lÝIK Ó3³äpzË  )\v†ÍÃé­ô2®'_¢î32³X§þ" ÃÃ[e † ‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹«˜Á0yŠîkÕL çþi~Ç/Š5K°¿ëˆ¦=w³z´k«Ö=®×“÷+ç’Uå){ñ8ulšï[iä‡{åºÜ•*¸Š ËÈuà}úëOÉPêÉ-šüñDm#]°(KCÃ7@FþÉð ÿe­\>—»—ƒ­òpýûÍ };c‡’|ëª÷õ׫–ýr× .+#6ߪ/Uï2Vÿìr‰êÈ //yfìÓŒÇëÓ)+µëd†<‚"Õ¸ã ÝrïÍêVÍ»ðÉSt_·'573÷?½:>£¹Rh^ºÌ^Ÿ¼Z»ŽÆËÞÿ Ýzð¿z{c¶LI²…jÄø?ôb7çYÕØ©wG_«ñ;òQÚkë®ïÕCÏ­±yê7Ý=ðY-LÍÉÇð馗f¼­a!…c·ûøºmÀKZž™»-|¤>úcœ:圹®¶ÀAzgÎKê’¸B?|ö&/Ø ½'•ã SÍ&5èæ;4¦k5y+]ûæ|«Ïž©%èTºMþµÕ²ûpÝ~çµ¶dogàOuCCžöúâ®ÿèƒ5Irç|j¯VL|[«æ/ÓC¾­;šú–¢lS™jÂ÷ËôÃ+3u4§ÀÔ§q‡´iÎ7Ú¼`––>ÿ…^Yèbæly_7Üð±vçœ5]jv²NجS6kñ¤_4ù±÷õÖõå8Ïñ3¶nÔ¶˜-¼ïúlsJÞùÙU%0Jƒ†·ÖÇ›–)Í”äŽ×‚Y«•Ö­‹| ”àÚ;O³÷äϬcÈ£Á Š.ºß¬Ö[#»¿©ÅSãå–d¦¯ÒÌEñ:4¤@‹¬[§ÌÑú¬üó²+òêÁjã””|¦,wÊ:-ù“~ýßËšwÊ¥ÓW!û„v.ýMÿ[¾P«Ÿ~OwƼ »ßߤ÷™}ãnÒœ¯6kÑ¢ízç›§Ô5¨díÁJæŠhŽIû±Þ_[ àN\­·Ÿx_«ÓKW¶™ñVTÝ‚ÛÛU¯K'E(ÀðRÝÊg>0³2•Uê¦^ÅqEC[P¨‚Î:[XeU²趘œ¨¤R ›ÂkT=gr»§g¡™H íŽÑünÔ°;^Ô'“—jóSJJÏ–Ë,MlŠlÖTaçM_Q2ü*åç8wìbÍX“!¹jÞœmÊi 鮡嬇­Rì÷p˜J[1K‹M™ K5kezÞ¹òi7H}*Ÿÿ²U­¦ª…Z yzy¨ƒ]áágÍèê%¯‚Y’P\rWD04]n¹Ï¦ f¨„kžÙÑáô)Ѿ) ßÖ¸w)5?”ùGkÄcoèë_&jÆÌéšùÅí*rXÞyŽïåí}ãÛT¥ß0uò? µ`楯Ù[ròr•]•{T{ŸórÖ!ÕmDoUÊ{:Ì´Uš¹8^ Kfke~s¡-@]÷PÈ.ŒátÊy¡ gòòº&Æ*¸+â­ÜŒ9®S¦Qà3×±Ã:î:“ ÿ@”rÕÃ(ÉŽÙÚ°`ñ™n«†:üß;z~tåÓ)Ü•éSĺ‡¥gw×Èž¡šû{ŒÜr+nÉ-ˆ>¨-ùÍ…öHõÒJÞ.¦Ÿ¶Ã5¨Æïút¿+wŒæüEZ`_“»4†$[p7 îr±H£”aÀŸéŠh1Ì9´\Köœ×Ó¥ƒóçiÇél ¨uþî˜åÊ¥¤„”3= mjÜ0¬À…6•ºs»–ë„*¾ê8²Ÿªå¥MWìMš·EÙy•ð¨3@ƒ›xžµ[i±‡´ÿhbÑ“òx6Ö°¡Ñy“ИJß9SS×§žn ï3XJ³4$€¿œ+"*g»>}êuMÝtTñ §´cÆëzüÃÍg&-±ù«]ç"ÖÚ»$ì ò?ÓRæ>® kç…/S©»'ê¹7ç*¹œgÚôj>\CêåMB“³EËWç­ëhxªá€þªWh@dœþçZuï1HýûöTï;¾Ò¶Ì³K´«öàaj7xÑup…VÍ«´½ªúnuθKÓÑ•Tr+eÓ7zäºoŠøÎgí‘ÓÍÿOª‹‡µi!¿ŸgåNvc¦iÅG«÷¯õé§=;*ÙmÈn3ä:g`dØëhèðæúø?k”if)3¯Ððj®ÁjºB?ÿ²SÉ.SR¶N­øA“¶Ý †- ?¶ˆ¾Ñùm-Ÿ•,3;ëtË¢GÔ jzv $€Šê h1´+¼acUñ*j4›!{h=òÊÝj^’veb(¨÷]º³•ÿ™‹kfèÔîMZ¿íˆ’ÝÞª{Í«zý¦jå:ÎP²)²ÿuò-x 9Û ÒÕUϺÍvo9 å:/9Šº~F°zŒè]¸ ®á¡èAýÕ |+à2ªðÁÐði£{ÞþZ¾zVc»7RdCv/ù…×S§Qÿ§~zGc¢ÿäN^õuë_éÕ;z«id ¼ívyú„ªV³^ºýåoõýÓ}ÔkàÕªUÎáÊí®aÝÏÜTÃOõP¥³3Ÿ³£îxdšVò•3 º:Œý‡®‹.º2¾í‡¨•3ß^Í4x@­rµ.'Ã4‹¿¸Þ†íûT?ªÚ¥¬ÊÂ}\ßÝ6X/¬Ì”)É4PoMY½ËЋÖLœ¡‡û?¦é‰¦$C>ŸÖŽÒ–/ð'۹ﰚGG•z^ï¯ i¾Õwk³òfµ©RïAêX¦¡•ÙÚóó7Z”÷Ûá§ŽCz)œ§¸¢\!“ÏXTv’b“ 9¼³t|ÝïzmÜ7Ú›·v¡áQG#¯m§â®iŸÏ•«x—SN3NÛ¦×3ïmTþšööˆþº®w0kW‚aæ:ô³îþ–6åœõ…á¡£ÖØ†%½½¦’f>­^O/>wmC[˜z?t§:8K__MÃ+ᩪ=×ÛvR@95íöPµ»ÿz~`8}€+Á°3¼U)ÔOž1é’3D5¶UßQÓØ PÊg÷ U¥oOuÉ©úWõЈ±c5¬E( p…bVR¨à˜•P&C°8‚!XÁ,Ž`G0‹#€Å Àâ†`qà +G›¿A=ùV¥_Ȇ`qC°8‚!XœÇå®@i™™§´aÁ<-\¿WGâ3d:Q»±:vë¬v5|eäoè: ßþû™¶5¿M÷5:¢é³WkËþX¥º½¥Ö½ú©oãàÓÂuxƺPñµèþ›¯RØy¢³ûäBýïÅŠówuK™§‰ó7k\† gˆj6n«þýÚ«¶_~-Ü::ó=½:Ç­ÞÜ¯Õ êÒÞIÿÓ;K5ô±ÛÕ=Ä”£˜­‹5cáFm?§”,ÉË7H5ê¨}ï¾j_ÍûÌùÉ&3y—fOZ¨•Û(6]r„TU£¶=5¸kmý\DÅ †Y‡5óã/4í¨C ÚvÔÐêþ²¥žÒ¶U+õÃÛ´ÿ¦Ûum#¿áÉTÌÆ‰zo•©JWµR¿–~2RŽjýâšùõ)¹î½Wƒkä^Šì˜#:’š©¬ƒGg^¥° V$C»¦}¦®`µj×[mü¤¤ƒ›´hÅzï`¢î¾÷jÕu,¡(™»§é½/WË]·ƒz«¦@#]ñ1ǵow’²¼×¤ß?ÖûGÛ©eÙÒNiÛªÚ¬ö7õTï‹—T”Œ-ÓôÉÏ[•åBOQý ÎLj“¯ó¤¦|øµŽµk¨ê6%X¯ùËÊVµ›¶Êkµ4äÛ ©8wió´_5˳«šTòRfÜ!­™7O;äU uÓ­#s¾ÔO‡CT¿F¸ÂBäíJԡ˵&ÍWMšÖ’WéN ŠTƒ¡$Ï*êyë]ª´h¬[¦‰«ÒäòôWåZ-5zd7u¨éWên¤žaÕé³Gñ5"rÑB ¶©kü6jÊ¢YZ›)›Oˆjµ¤ýÚªVpj´Ð·eiòôZøãgšæò”X¤µ­}—êõ_ÒO—UWá»×kõ‚õJÊpÉðôUhÕ(õº±§z6óÿºÈ°Ã,zö”"mؾOõ£ª]ÊúTî“ õ¿7f+³Û]úgÿÈ Ø'€ÿgï¾ã£¨ó?Ž¿gûf7 Cé`¡ ˆ»ˆØ ‚í–ÓÃrÞï¹]®š. €Ugƒ¡aÊÊÎQN $_‚G‰>œ‡l6£¦KÔb>¯[^·KI‰ ÊÊ jofŽ|Þˆý>z–UgƒaVvŽÁ¤&ÉëvÉngº$àÐ ÃÝnÈnwÉépÈëqi÷ÞLI’ß—PÃÕP3êdš …ÃÊ „T/%I 7¡P)v»M ·ê¥$)'R(®é’¨u.Q†¡½ûräózäu»: ¨›Í×í’ÏëÑÞ}92 þ_XO  IDAT†¡pX13¦DŸ‡žB@\Øí6%ú<Š™1z –Tç’UVv@~oþB3Ä‹ÓáßëQVv ¦K ÚÕ¹`†”àa) ®l6C x.À‚ê\·[n0Äs qg†ü å Tçz ñE0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,ÎQÓÔv߯_ iéßè—mk´iïVmÞ»U’tJó®:&¥‘z·ï¥žÍ»(Ù“XÕ@å ËñÙâ‰ziöÿŠ‚à~X¿P?Hú|ñ$%yüº­çuÔ³@ÃPÒì fé‚×oÔqO• ”ÌÖ‹³ßVÏ¡Whù¶ÕG¸Bˆ/‚a1Ë·­Ö¯ß¤_¶­©Ôñ™Ál]óÞ]úlñĸÔóþ‡«yëãÔ¥Û)qiïP^~å5µ?±‹þóâÐÛßsŸš·>Nƒï¹¯Bíµ=¾“š·>N_ŒÏ2«Åˆ×ßRóÖÇé´^çÖt)åªì}©.7 ¼MÍ[§þ[M—€C`(i}Á, ù×Ãî%,Of0[CÆ=¥ÒÚêÄ´¶qª®z|óí\ƒAÍš=G=pM— Šæ-\¢·ÞûDíÛ¶Ö{n«–sF¢QÍþæ{ý8o¡¶ïØ%‡Ã¡´£ÒygŸ¡“:w(±ï¶;5kÎwú%}öìÝ+Iª_/U];uPŸ ΑË嬖šÁ°ÈÕï®r(,îš÷îÒWw~¨cRÅ­Í#íÁ!÷iÔctå—×t)ªh÷ž½úø³±r8ªïŸù¼¼ˆžyáíØµKÝ»vÖé§ôP8/O?þ¼Po¼ó‘®º´.<¯—$)++[Ͼ8\©)Éê~R'ÕOMU^$¢¥ËWjòW3µeë6Ý}ÛÍÕV;VG0TþB3+¶¯k›™Ál½8ë- ½â±*·eF*:´Szž¬Szž\-çp䘦©ÿ}0RM§)7¨¶ó:]yéEjÒ8MõRSж÷:½§{êM1»(&&úõä£))Ñ_¢³Ï8E/ {CK–­P0’Çí®¶ú°2‚¡¤g½}DÚµd²8û¶*÷ºÝnåää襡¯hÊ´¯´sç.5¨__^pžîûóÝJMI)±ÿåWõÓâ%K5 ?=óäã¥Úë7à&ýôó¼R¯¾ç>M™ú•.ê}F ù°jÛ½{·†{U_OŸ©]jØð(]Ú·î½ç.yýl”Ö®['C†Úµk«AoVß‹/*ó|_}=Co¾ýŽV¬X)›Ý®'ž ?ß3XnOÅ?Œþû?/jÄo«eËšùÕäR¯6\C‡½Zêõ÷?üXÿ|â)ýé¦ôðCCôò°W5qòíØ±ó ÷ù`¢Ñ¨¾3N£ÇŒÕÊôÕ ‡ÃjÜ(Mçžs¶nô'¥}t©c2224öË šöÕt­^³F9Ù9JJJR×.u×àÛÕµKçRÇD"ýïÝ÷õÅè±Ú°q“ü~¿N?í=ôÀýòTâ=D|Lùz–6lÚ¬G¾OÃßx·Ôë;vfèÑ'Ÿ×}ƒoÑ®Œ=š1g®víÚ­ÄD¿ºwí¤Ëû^¨-[·kÜÄiZ÷ÛÙm65;¦‰®¾¼ŽiÒø çîpBûRÛÜ.—šÓDK–­P^^DNgþ=†B)?ÔæåEäõzäv¹Š¶/_±JÓg«M›W RrR¢š7kª‹.8GÍš¼&ph–†ß¯_ -û¶Uúøk:õÑÐ+Óg‹'jȸ§J½þÖ#õ¯‹þR•%Cº¶ÿúeÅJÕ«WO^¯G[·mÓ{|¤Ùs¾ÑØÑŸU(4ÄKFF†®¸º¿6nÚ$IJðzµg÷xãm};÷{%x”™™U¡6ç~ÿƒîò$)%%YÍ›7×ÖmÛ´`á"-X¸H;vìÔ·*u\úªUú¿Gÿ©‘Ÿ!‡Ã!Ç£ììl-Z¼D÷Ü7D™™™п_‰cÞ|û=óÜ$I6›M‰‰þüÀ|ãÏ:çì^•yKªdá¢Åpß´xÉR¹\.9öJÝçH$¢Ûî¼[³f#Ir8r»\úmý½ýÎ{3öKýï­êÒ¹S‰ãF¼ñ¶Þ~ç=IRÚÑG«^½zÚ´i³¦Ïœ¥Ùß|«‘¿¯n'u-Úß4MÝyן5}欢óD"y?a’æ|ó­Ú¶i§w±~ãfM˜2]ý¯¹Li R4+wß/¾œ¬p(¬ÓNé®D¿_ /ÕW3¿ÑŽ]J_µVÝOꤓ:_¬Œ={5}æ·zqØ›zæñ¿*Áë­p]{”’œ\ E"åæ ‡µs×nÍ™û£¶lݪ›\S4ZbÕšuöú;jצ•®¼´ìv»vìÜ¥é«åq»Ê:¨ ˯J:uå7•>¶0J*·WðÇ +Ý~¡­[·)kú´‰ZøówZ²à'½9b¸Ün·Öoب?ÿb•ÏQÏ<÷mÜ´I‡CÃþû‚–/™¯ËjôçŸ(c÷nmÛ¾½Âmž{öYºç®;õå˜Ïµxþš>m¢ü4W^pž$éµ×ßP4-u\ Ô¨ÑcõÌ“kÅÒZ¾xžfL›¤–-[H’^yõõûÿöÛz=ÿŸ—$I§ÚS?έ% ~ÒÒ…?é–?ݤ3gW¸öªZºl¹¶oß¡Ï>ù@+—-Ô/Kèå—þ#‡ÃQ¡ûo [(ÖÛïªÇ·Ó™§zXøÎ]zdÈÝêsÁ9:ãÔº÷ŽJMIÖ⥿¨Ï…çèÆë®Òé§ôÐe}.Ð%}ÎWn  •«*>ì>}õ:mþ}«zVº¦%ËWêÁGŸÒßÿõ¼†¾ö¶2vïÖ}ƒ©{×ý?¸X½öWIÒMý¯Òi=»©g÷.º´ÏùzdÈÝjxTƒ ×J³|0ü¥’Ï, ÷³ôøÔ¡eîWÙG_è±Gÿ¦Ö­ZIÊŸsxÁùçjàÍ7H’ÆŸXá!›U•h¤)’¤ëú]£K/¹X6[þ·ÓI]»èá+×Kj†rŸ:uÜ¿z¡ÇãÑ·å÷fffé÷­e/Ôç¢ 5 ?¹ †ŸµjÕR÷ß{·$ië¶mÚ¹sWѾ£Ç~©H4*‡Ý®ÿóœ6[Rþ¼¼Õkâ»xΡ¤§¯* )—öíSêõÞ\PåsìÛ—©-[~×ú K¢@ Xæþ½/<¿Ô¶6­[}•µÿí’¥Ë$IÝ»w+5ß.?xŸW¥Ú+ë¬^g”ÚV8?òpîó/+V]çe—ö-õz³cŽÑI]óç ÎþæÛ2Û0MSÚ´y³ÖoØ(ÃÈÿg"Xì}ÅbZ¶üIÒ%eÜÿƩÉ'´VÄ×ÂÅËôýOó5ð†kåó%Ö1ň)äp8d³ÙJÍÿ+\Ý4+hêvïÙ«—†¿¥G÷Þ1°Ô0R)žá ǵÓI]:êü³ÏÐ#CîV—N'jòW3µî· ’¤N'§×\®µ¿nÐcO¿ †½¡ç-,sô¨ËÏ1,o~á “ûé_ý¥ÔÜÁІBIÊ Vì'ìòû}r—±2_ZZâ¯322ªtŽŠÚµkÿùš4.½ðƒ×ëQrr’öíË,±½[ÏÓKõ8 ºåfýõÁ!’ò‡x>Bs¾ùV»÷ì)óܦi–¹½QZZ©m®b‹W?nWÁûÕ¤œ…4ÒÒJ/Îòþ‡ëégŸ/µ}ñ‚+5çê@>ŸO~éÅ87Ú?LùP÷yÇŽE_—u_$©i“&úyÞmÛVr¨ï·s¿×›o¿£yó(,¾‹¿ÙÙ9 …Bí•ÿpY{÷íÓ‡#ǨÇIÕð¨ÊؽÿïN4S$QÆî=r:%ŸËYösm6[•WCÞ¹+C/ K‡]üù%%%Öq†a¨g÷®Z¸x™Öþº^­Zä÷ÞŸuÆ):­g7Í[¸Ds˜§w>üL“¿š©ûﺵ̀ *ÆòÁ°<ÉžüOý:ç÷º ÷T¥Ba<„Be CE_WäC\Yú+ªxH°;ìeîãq{´O%ƒa(.52’‘$­[÷«.½òZåää¨iÓ&ºáúëÔ¼ysyùùm»ÿxqd¤¯þU9¹¹úiþ"ý4Q©×÷îÛ§¿=þœN8®­î\zñ¦xÛº}‡þ;ü-ùý>Ý×­e®>z0á‚óœ<ƒÑétêÔ“»éÔ“»iñ²zí­÷5yÚLÝpÝ•q««²|0ìylý¸¡ô©—æüOMS©_ç¾ê×¹¯ŽIi¤S›çϹ©h(,<®²B¡öíËTrrR‰í[‹ÍµkÐ`ÿ …á!VÎ0«íÛw”¹½"êÕK-úzÛ¶í¥†cF£Ñ¢^¹â–-ú¹Ü6ÿ÷îûÊÉÉQjJŠÆ}ñ™4¨_ôÚ/+VV¹æâêÔ¿u[Ù äl/cáœ[Þ¤[ÞT~£‡zßwü}…BÚ³wo©•G7oÞRôuñû\–Æö÷šnÞò»7.½(Ò–ßóÛ+ÞÃúòð×dš¦NîÑ]Ÿ~ô^‰ù[·–îUOLôËét*//¯Ü÷ðÀI9íÚ´Ô]·–ý½ùáÈÑJNJÒ¥}ίp@«Œ›×ÐWßÖÑ èÞ;–Û›ž••-¯×S4DµP,Ó¬o¿—$×>e[Ó4KýP¤ã íår9•›{®ë±|0<&¥Q™ÁPRÑÒ~ûV:6I.=¼±2¦ÏœU´I¡ÂÅ_¼^O‰¹t~~ÏÙ–ßKÏŸL_µºR«…èøãÚ<ž ¢)S¿RçNK¼>÷»ï+<ÿgÓæÍ’¤.]:•…’´"ÎÁ°cÇúîûõãO?—Ææ|3·Âmú z,·ïØ¡h4Z¢'-‰hîwß²3féê«®(±mâ¤üg&x½%îsYÚ¶m£FiiÚºm›ÆOœ¤ÝKþPbóæ-Z°p±$©gÏEÛ7mÊïÏ=ç¬R‹Æ”õÞÛl6xÂñZ´x‰¦NûJƒï¸µÄë{öîÕ²eËZ+â'5%Y©)Ée¾öÙ˜ òû}êÜñÈÏùü}ëv½øÊÊ ç©uËæš1»ôߣfM›¨S‡ãµdù }ñåduîp‚Ò¥¤$¿23³5oámÚò».:ÿl5::¸üÄ©3´~Ã&µnÕ\õë¥*//Oó.U8œ§n]:–:¨8Ë/>sJ‹®}}ȸ§ôÙ≒*7|ôÔæoÿp½4t˜–,Y*)ÿ'ê㾜 qãÆK’®¸ìÒ¢•'%©Ã‰'J’¾ÿáG-.8FÊ_IôÙ¿—z|>Ÿ.ê¿ÀÌ}¬¯§Ï(^¸beºþõôsn³aÃük×þZbÈæÆM›ôâ‡Å¡êý®¼ü2Ùl6…B!=üÈ£Ú½{·¤ü÷hØðZ´xI…ÛìØ!ÿ}‚zç½曦©W^Qb^fy^:¬èܱXLcÆ×—«‡^yåå%îsYl6›n»u $iäg£4ê‹1ECT7lܨûxXÑhTõRSuíÕWwtÁð+ÓK´7kö77~b™çº¦ À.YºL/¿òZÑœÃ;vê‘ÿû‡rC^/þX¶lݦ@ ¨H4ªi3æh”é¥~-Y¾B’Ô®mkõìÖU¿®ß )ÓgéÑc4}Î\¥¤$é®ÛnÖ—ô.j·}ÛVŠÆbš6cŽÞùð3™0U±XLwßv3Á€8±|aïö½ôO÷•Ê)wŸ!ãžÒ¨Å“õ˶ÕÊ f—»_Y®írqUKT×.•™™¥Ë®ê'ŸÏ'3+úÐÝ¢Es=\°pK¡¯ï¯?©}û2uu¿ëÕ¹SGy½-]¶\ápX;u,+ëÑÿû«,\¤ßߪÛî¼G ^¯ ›M999:¹Gw9N¥ðˆƒƒpݵúòË Ú¸i“z÷½\ÝNêª;wjîw?¨KçŽÊ‹Dâ¶ÈN›Ö­4äþ{õÂK/ëëé34cæ,%'%)++K¦¤®ï¯>þ´Bmžvê)êÔ©£–,Yª§Ÿ}^£Ç~©´£ÖšµkµeËï:¥çÉúáÇŸÊ=¾I“ÆJðzuÅÕ×É— Ó4‹îs«V-õÐû«Ž7ߨåËјqãõÐ#×ß{B^¯§h! ¿ß¯×†-±È×ÐsÏ¿ q_NÐï¿ÿ®æÍ›kݺ_µ`á"õé}¡&OVê<×õ»FS¿š®o¾«ÿ¾üІ¿öº|¾íÛ—© êëâ‹zkÒ”©y q<ûø#¥¶5<ª¾Þöï2÷¿ó–ÊÜÞ²y³r)Ô½k§Ï<˜£ê×ÓuW_zXû¶iÕB÷ßuäçF`e–ï1Lö$ê¶ž×r¿Ö/¬p(ìyl—*Ï/”¤‹ûôÖèÏ?ÑM7 PRb¢ò"5JKÓÀ›oÔØQ#•rÀ²FÒ4vÔHõ½ø"%''kñâ%J_µZgœ~šÆ~ñ™ú_wm•k’¤£6Ô—£?×€þýÔ°áQÊ‹D”šš¢»î¼]¼û–zt«ØµŸÔµ‹F~òN?íeddhü„IÚ´i‹î½{°>|ï:ëÌÓãRw¡{îºSǽ¤Î:Êår)/ÑÉ'w×Èß×ûï­p{6›M½÷¶ ¼YÇ4mªµk×iÁÂE:¶Y3½õúp=÷Ì¿z|,ÓèÏ?ÕÀ›oTRRRþ}n”¦[þt“Æ|þi©9¦å1 C/½ðo½2ôEÒódù”›P³cŽÑ ×÷×Ô‰ãÔóä%޹óöAzñùguÂñÇiÙò_4iòT™¦©—_ú^>´ÌÕ[m6›þ÷ækzèûÕ²e‹‚mv]vi_3J_Ü»Ô1¨ óp—T”´$ý7µmÑôHÖsH«Û¬Ní[ĵÍ}Á,üßËÚkX£þôj\‚!þØÞÿðcýó‰§T/5U çz"€#«6ü_@EU5'Y¾ÇPÊï5übà%ºÿq‡òxïû …¨ üŒˆ+‚aÓÚꉋþ—¶®éÔç°†§@m@0,¦_羚vçUê9|¼÷ýzÅcq¬ Ž,‚áNLk«¯¤k:õ©Ðq=í¢Qz•žBu‹ÏĦ½[õÖ#õÃúZ±}m©×›$§éÔæ]um—‹™Oµáÿ:*ªª9ÉòÏ1<˜cRé_Åæ.ß¶Z™Á,I"øÃ VÀ‰imkºˆ;æ€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâê\0L𸕔iš5] àÄ4Meç•àq×t)T»: ½·rsƒŠÅ†€ø‰ÅLåæå%,¨ÎÃD¿WÙ ò"‘š.ð’‰(;T¢ß[Ó¥Píê\0t»\²6eåÆjºÀ@4SVNP6Ã&·ËUÓåPíê\04MS)É>å‚ „ )TI,f* +'TJ²9ìKªsÁPÊï5ôyÝÚ½7S¹Á=‡€J‰FcÊ †´{o¦|^7½…ËrÔt••è÷I’víÉ”ÏëQ¢Ï#§Ã!›Ía5\ ¶2MS±˜©¼HDY9Aå‚òyÝJôÓ[°®: MӔߗ §Ó¡½ûr”•“+¿×£„ü žš.PKåBÊÍ *;?§0%Ù'·ËE(XZ †…Ü.—Òº ‡••о»• ÕtY€Z*Áã–×ãVýÔ$!ê|0”ò{]N§ê§:kº@B( _\|?C°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`ç¨é€º (ÈU0V8®–sºÜ.yÜ.y½ òz½Gü|5quIußêD0"(+;[’)Ë­õýòx<Õrî`0¨` ¨¬ì,eeg+Ñï?"¤ðMÓ”ÛíR½ÔÔj»ÆCY¿~ƒš7?¶¦ËTp?BAefÙûQ†@9²23 ‡”àõÈëõÊn·Ë0Œj;¿Ûí–Ëå’/U PNN¶"yyJLJŠÛ9²23 …äu»å©k¬K ïG4!ª`  ììøßšB0Ê•™©p$O‰‰Ir¹œ²Ùì’iÊ4%Ó4«­C†§|>›œN—²s²••™—0’•™©P^X~¢\.§ìv{*þã2 C†aÈf³Éf³Éát)'7[ŠÓý¨I,> (ÉïóËívË0l2MSÕ÷3•D Ã&·Û-¿Ï¯P8¤@ P¥v€‚¡| ~y.Ûl6y<^If¥{©€LÓ,šSˆÊ³Ûíòx½2ÍÊßÚ ö}§5(È•Ç宕¡°Íf“ÇåV [©ã\¹Ý.BaœØív¹Ý®JßÚ ö~·5  ËíñÔêᕆaÈíñ(XÉg CayܵûëÃ0äqWþ~Ô¬J …åq»kd¡™Ã•DÜ•~}8®5Ï)ü#0 C§Ò÷£6 ¨h(LO_¥Y³ç”ØÖ¸Ic]vIßr™7¾æÏ_Xb[ûvítöÙ½ŽHÀÁ €*xôküÄIe¾–™™©¯Pj{zú* ºmp™Ç´k×Vï¼ùº“ãZ'p0Ì1*)=}U¹¡Ðç÷«ûI'•ùZ“ÆÕ¶m›2_[µjµÆM˜·QÒ¼EËôå¤éÊÊΩéRjz €J*>|té¢y‡}\bR¢¾øì“RÛot‡æ/\¨Y3ç”ÙÓˆª›·p©$)}ͯêÞ¥C WS{ÐcG0‹#€Å €JjÔ¸‘¤ü…fâ¡qA{…¿Õ…Åg€JºüÒK”••Uîê£õׇP“&uÀþqi8\C  â¹zhbR¢ßy{ÜÚÁÀAÍ[´¬è1••è÷©ßå}är»âTUÙ·Öy —–»_uÕZ›0ÇÀAmù}[•ÛÈÊÎQf5ÕUkmB0pP‰~ŸºwéPÓe–CÕZ ›4N«3×TJ TAzú*eefÅ­½yóçÇ­-àp €Jš7¾®íƒn¹ýθ´7âõ75è¶Áñú›qi8\C ’æÏ_(IZµju\Ú›7oA‰ßêB0‹#€Å XF¢/ARþ#)°«*©]Û¶E_ºµä47ÒÃQbRb™ÇŽxýÍ¢9Š…Ò׬Éo·}Û²Aô>'EÎ IDAT»âbefçXî9…‡B0*éœsÎRÛ¶m´zõÍ[pÀ‚1 òÃáà;o/uܼùó5â·ÊlÓç÷ë²KúÆ¿XH’\n—¸]5]F­C0ªàÝ·Þи 4kæœÛ7n¤ô/ó˜îݺið·•Z}´]û¶ºì’¾j߾ݫ( Á¨‚ĤDÝxýÝxý€ 7øÎÛ58>?ªŒÅgÀâ†@1N·KÁ`P¦iÖt)å2MSÁ`PÎJΕsÕk¬K Ï]$Åx].Cµ;4™¦©`((¯«rAÄã®ý×X—ÞÁøcp{< …ŠF£5]J¹¢Ñ¨B¡°ÜO¥Ž÷zjý5Ö%…÷ÃëM¨éR*`ãr¹d†‚@­ NÑhTÁ@@†aÈUÉC¯×[«¯±.)~?¼^oM—SiC Ã0ä÷ù …çժᖦi*ÎS0’ßç“a•n+Ñﯕ×X—¿‰~M—S%<®(Æf³É0lò¸ÝÊÉÍ–$¹\NÙíö­+*ÎSNn¶‡CÙ99Ê åv»äq{ä©ä¢/ …  …‹†¸†M’!§Ó—s$&%É(+;»F®±.9ð~$úýu¾§°Á(ƒËåR8V,fSjr²Âyy ƒÊÈÝ£¼P¸Zjpº]òº\òû|r9ŠDc²Ù*¿èLy¼^¯¼^¯€\íÞ³GájºÆÃ±~ý†š.ARþó=n—’ÿ0°Á(ƒar»ÝÊËËS$•Íf—7Á'o‚¯Fê‰Dcr8ìqë),Ka@„õ €ƒp:G4Œµ«‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°¸ CÛ¥Ü`H¦i©z‡É4MåCò¸]Uj§BÁÐëv)ÉŒ  ¦™1SÁ@HÞê †‰~¯rCaåE£U:) êò¢Qå†ÂJô{«ÔN…‚¡Ïã’Í0”)UéĀʋEcÊ „d3 ù<ÕØch³ÙT/Ù¯@(¤`^žb )€j‹™ æå) ©^²_6[ÕÖ­p0ô¸òy=ÊÌÌV0¦çªQ,S0Vff¶|^<.G•ƒ¡£";Ûl69v¥ø=’¤½™ÙòºÝJðºå´ÛeØ †Q¥‚%™¦)3fæÏ) „…äóz”â÷Èn·W9f%ž= …ò».ÃíÞ—­˜i*Áí’ÇëV‚Ç]¥‚%åC BÊ …e3 ÕKöôr»«žÁ* MÓT8V,fÊf3”ÊSVv@PXÁP¸ÊEöó¸]òº]Jô{•àve1—Ë—Q›• †…òòò‰ðè ¨N‡]N§3níU)JR,S,S4Í÷ÊB¥W†!†Q4Ÿ°ªs Kµ_Õ`¨Ûâ3uÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†`qŽx4’Ô¾¬åBÊ †âÑ$ @‚Ç­¯[ɉ>ù¹èM™¸H¡¸ž)¦ ï\¯¶mŽSóö×jĺ²ûô _’’FáŸd$&+©dqÚ0æMÙ‘¹ÿ(ÙìvÙ C¶Ô³ty¯$ªæn¹£»Z´>N­ÎzNóËêêð‡â¨éª.ª•“§jmAF3l6fL13ª­_MÖÏí¡3<ñ:Õoš0~©ò̃ïfK»JϾÔûSÒµÏ×FÞp½ZØKÖ¼6}"…íØRtö?>Ôðë[ËޣͻbjäSÍÛ1]c¿ÏÑ!.ÀH݆‘åš8u}Á>»šœwžêÏš¦%yRlç ÿáaqvB|N•>QãWF#4¹Ô¬×@ý£Wy¯› ÃûÛ±·Q¯s[Êg“äIU³¦q)·bÚ2e‚~ +©óÁ0oÑdMÙXÐ]hKÕé× R£M3´deDŠíÖŒ‰?(ûìsUº.ª%/\¢«^ÿM9ÕãS5òæÆÊÛü­Þ}å-úv…6ìŒêôg¦ëÝ«½Z7åUýã鵦pôhd™þ}á‰úwasîÞzmÑÕÇ%)k¼nëùW}]0ŽÕuúúþÝkÕÀ›ÐÈ7GèÍYáb1OŸy‚ÏoH½ü£F\œßÍÙ®Y“§êëoçkéªõÚ’±O¨KþÔFjybwõ ºé¼Vò•7î4¼M?©‘S¾Ñ¼´m_P¦Ó¯MZªC·ÓuÁeWè’å mѬ7ŸÖ?ßX PA.Œn~_W·¿¨)ç)éÛ÷û+­`rî†o4ò£±šúÝ­Ù”¡¬°äò§*íØÖêØýtß·¯ÎïÐ@®ŠÜPÕ®ŽÃæOúZ[ ‡‘&öP¯îÇ©q¯fze寊(¦½s&iNæ9º8é`3ö"J_ò‹v/›¢?zI?ìŽå÷æ JLò)cÌ_tÙ#³•SÕŽ4s‡Fÿí=ñÃa.Š“û»ì}²õÀ¹ŒíݾN ·¯Ó™cõÙ%Oêƒÿ\¢fö’{åmœ¬G?¦ÏW044š©mëkÛº%únKÑí ýò«të˜}‡9„4¦3ŸÔ€û>Óš@É#"û¶ë×¥ÛõëÒïôå‹ôòŒ¡º4¥FfK8Lu{ñ™À|Møz{Ác %t;S'û:þ¬ÓÕ¨ $ÅöÍÕøÙ‡ <¦r~|[·Ý;t(”$[’ê¥Ú%W=Ûº•š$Ù÷/cxÔ ykµmSð«uš™ìJnÚJmÛ´R£Äbo½áUÖ…mµR£Â†zê¦þÇÉeH2 9¼õÔ¤U{Ðúhùìû˜!­Ÿð¤ž»C±â§Ê™¯ÿÜñ·Ò¡°8#Q½®ºPGÛ$grµisŒê¹ö_„áLÑ1mö_c›¦Ér’òëõ§Fí…†S©Ç¯ÎÚ«ÅQ^å—fW£‹ûéÖGÛ¢’™­¹g)ã’+Ôà %ºc©I2ì‰jqòYêÕ©¹ê9ýêÔÌ®=žÖäKšù×35hô¾üìm4èO5¸•½üFdÔ×%ÏŽÕ% jâ==uÏÔ‚±¦Žºûýwus£sº]ínú‡žómÐѧ®­Så”$Å´wÞ ºî¦÷”žgJf¶¾›:W{¯ºRõ IŠê·‘/éýµ…ó ¹š÷Ößž¼O—wª¯è®ÍZ»ügÍø6¨sÎM‘!éÌ¿ÖW‘%z¶÷õzc}~Ô¶}™þ;áu;à»$–‘®E½˜†µK1™Êýa²¾Þy¹ú7KW8KÍm’­Á¹zðÙ4ð¢njÛ8U>—]†a“Û¢FmNRïÒ›_ŽÒ¿{§)>ý§Ž$Ã4ÍÞ·$ý7ujßâHÖ¨ ªfµºÕcˆ;‚!XÁ,Ž`G0‹#€Å Àâ†`qC°8‚!XÁ,Ž`G0‹#€Å Àâ†qÛùžûËßôè¸ ŠÖt1‡PõZMåþüžîºïq½»<çêòÅvþ¨—}JϽE±#r†º,¬ô1/hÈ“£´ ˬébðà¨é*#¶u†žyþ+m,H5†a“ÃãWý´ctüI§éÂÓZ)Õ^³5¢ŠÌˆòòòŽÄdÙècîÕ/߬’«kµI4J¼Í‹(/œ§¨eßü{wÅÕ·qü;»qâA,xðâ… Å­.”ÊKû”¨S·§Þ§@¡^ZêÅ­P¬¥hR,hÐ,„¸íÎûGB !¡ žìý¹.® fG~»2sÏ9sΕT"ƒa6¯zèÛØ‹™EJü "·nã¯;Ù¼ïvž½¿)þÆ¿ïEnL–òxúÝö`8î—hžÞÆüy›hX§e¾`èBƒÛŸcÂm†#<""""r•è`èØ€Nª‘Û8xK'þúr¿nùƒÅûqGM5–hzlÄlÛÁa4,ðu…B¹rJp0,€S9Úµ­ÍÌÝ<Y3Ãv©o}EDóÿðrçt–Íø¿v'Á¹¾:„FÎ`¦ŸdÓ²eü¹)’¨Ø4Lwk6¤S·Î´«V†<×ßfQáK˜½|‘'’Èrñ¡Z£ö ìdÁ9O1vŽ,œÀ[‹ìôzj4«œÿ8§È™ðáJ†¾ô¡çŽyz.]ÃúG8‘˜ÕÇr•›0èžîÔ/cä”pœ K—±ìŸýDÇ¥a”)KÍÆméß»5ÁeΫ¶Èµ^Dæ)6-úƒ?6D}&'ïŠ4hÛƒ~Nù>—3,Ÿô¿kΨ7†PÿüLn?É‚Æ17£cÇö¢šø—ï¥AìrÞ}wi]ãµ~•±öËy÷Ý=ü$7',gÞÊíì=ž@¦“k6¢G¿´®ä’÷[8³%ó³2"šØ '|kÓ¦[:g.àÅ_íÜýæý´ó($aýK!góÂÄpÊ z†§»øåù<²¶þ“ßl§Þ½¯ðŸ®€IâÚoynº…{_éƒëªÅ,ÞÉ‘¸ ,enÔŽýZìa€ý8Ë¿™ÂŒí±dš0ç±Ì°ÖàÎWþ›ýllùáM>Ý\ƒáo £ûÙZ¿dûM#rˆ9 Ö±-*Ž4«µ›Óop7Z°lÁŸ¬ÞEL x”«NóÐÞ nY ×ó‹7“9ö Vm'òDéV/‚j5!´O7Zº¢<*"""Rú”®`Xœ¬¹-ˆ&ä\ÄÚ9s|3Ó&…³É¹.-:×§Œ[N@F >ýš¹GÜ iÛ‘¡U½°$Ÿ$",Œï'ì`ÿðG¸§¡gÎ~²8²ì[>œw§ê-¹åÖ`üdŽìXÏ—“3°˜àR`UE“ul5“>™ÇN[š´nOgRãOï·{ÎåxÆ|öóŽûÒ¼swºVr#íÄN–ÿ9›œaÌèžÔt¹BµÚcYýígüa#¨yn )‹Kj »Â§1>ùJ´Æ^ä{)T*;ç|ɶ š·ëÂm¾N¤àï¿Wñíçixʆ嫘£mÎmÓ¾M~ûèk–O[LóÚƒ©ï fÒ6æ.9DFÅNŒz¬75r®¥[µ¾‰ê?Œã‹ÓPîRK7ϰvö"v¦ÑwÔúU-(¶Ù9¹z ¹Óö¡Ü×Ð#'°6¡±_:¯ý¼ŠyëÛ0ª½\ZÓwÿÅœˆ|[ßÇ“w†p¶1²mëZLýàK–âz©ï6WÁß ‰ÔN\¼/÷à³ð]hz¯oŠwÎ÷Ø*Øà­ÿbó–(n«S+µ‚ßV¢|×Gx²Õœo¸1-ëºóÁ{ Y°xí†5ÂãRÞ»ˆˆˆˆÜ°Jôt¶ôdâãÏ{ú$‡vofÑÏ_ñÕšxÊ„t¥kpÞV ýúÖÍsaǶmÑØ<ЩYÞ i¸Ó¥u F|dG€Ì{Ø“f¡Z«VT??·ehÔ²mÌùfÊ^6ïËÀ¹N[n®RH[žÏÖ-QØ|êÒ¢†é¤§gžž‰[ÍÚTµd²?2Ь+R«¨]‘$þ´lW‡ó{¨âZÖMÊ^‘ž¿—‹oKf´ð? eƒ«àmdræLZö(¦ö8"vœÀîYÿ‚ïÖ©bsÚ[‹Ü%²ø5^Œ…ê-šåmq4Ê\=‹=‘3—9ý„áUæµÝÎ[âJ•Êå°®ÔkV?7X+äl’”˜”3m‰#[¶s‚ ÜÔ´<äþ|eéU“zå-¤ì?@ô>‹ˆˆˆˆ[ n1´szõÆ®>ûoÃÕzïàŽ¾M)—ï"ÞR.ˆÊnù¢€=ž˜X;–€rT¸à“°P¶b9œ9NÌ©TL_à •-–¢?'w)5š¹aë‚#cµ^½á[ «¥Z‹2Š©™ý3o­Æ-÷¦‘[«X<Ñ#†""""¥O‰†®e«Ñ¨ÁyÓU—ÅŸ ì'Op< Êçi†²sêx ™d·.”ññÁ…CÄÄ$`’wJ{B fÞ¾¹‹ü29s&ßsïÆÇ?«IäÉS¤™UóvQ IDATÝÌ]ÉrþÌS™¸V®N­‹<âWÜZ øpðõõİÅrâ”òŒªj'áLb¾gô,Ùº @d&—h÷‹ðÊ2|ð±`?}ŠSYP>Ï0¬™$$¦^þ!ŒìvöÂ9&‰gJàäóVÊúb˜Id¹W¡V° ˆˆˆˆ£(ÑÏ^6ÃÆM«âœ¼ƒåâò3u?Ë×Ãô ¡Yõìüì\›Ú®v„oäHæù;Jgï¦ÄæIJ^>^XÌDNœLË–̤ÝlŽÌ̳Ìð¬E£êNdìcűB†5±øÓ¤INÉÛY¼ò™¯u µÄJ•º5ñ4cذîiç›u‚õ›å ††>^.vŠãgò&¢ô}ìHºÆ)É@ýze1#Xµ-1ïç&‚õ{3 k(.2ÃÛÃΩ§ò~f<[¶árÅsqqÆ0SIL¾VŸ•ªMPžX–n"FÏŠˆˆˆ8ŒÜbx%”oߟþÛ¾bÆôÏÝ–Õ¼±¦œd{ØZ6ÅùÓ~xwBrZæ ¯ÆôíÆî2áÓº¶¬†Ÿ%•ã{6vÄ™<ã¡`àÒ„ú{Ø2o* »Ð¸¼ é§¾t;M—¼= : ¸™ðIK˜óÉgD·kB-²’Osx¿¦ÃúÑÌÝBÅŽéñ5³æ}ÁÛ[Ъny¼¬6’c±o×ú=Æíu­Å¬µ`®!7Ó¯ÞN~^ù#¥¶§}Ý\ÒO¹q{²üpÉ󔢵›5Àwãüô^·4!ÈÃÆ™¨/ÜJªëÕë>Y0 UºÜB»?°ú—/±mKÓ@²â£øgÍv޹¹`d\æÊÖ§EµeL_?—ï+ö¥[_¬©§Ø³vuÆé²`Á¿FuXÉšéÓñéR?Ê6lJ-¯Ë«ûbœªÝÌ]]ö2鯙¼ûq$í›SÞÝ #é4Q‘{9UãVÆô tð;J""""¥ƒCÀ9#£üò¿X¶a3ÂRÈrö¦RæÜu{W:Ÿ?Pˆ3Õz<ÀS^‹™ý÷6N[ÍÕê;ð#Ù4þK6Ÿ·kç÷È`ÖïkùóÇ/™ksÆ»\e¶¾ƒgۮ⿿¦ä)Å¥jWF.Ë¢%alZ·Œ’2±¸ûX»~N9U¸ÑëÑ‘.ÿ“¥¶°0"‰ œðð-OpÝÆ4ª`¹¤Z d)KçGàñÇ"m\ÉÔ™8ûҰÞlt”/Þûëüw‹Gý~]I¦Õ²•kÑrȺlÿ’/ö_âwt‰ Ïî~beç-cõêlͰâU>˜V· £ÃÞoø<ü2`)O·á÷a›»”U‹äý9&®>¨Ù° ÿy4é/º¬Ý;wgÄm©üºd;óÛŠ³O]×nrUƒ!†;õŒàùª+X¸r+kn#1ÓÀµŒ/5êÒ¦Ž¯&¸)… ó¤ µe×šÔ ¾šõˆ\lüî-¾Ü^—ïÜM3çßBDDDDäFv¹YM=ÂÄád?c˜5°2•5¾Šˆˆˆˆˆº’Jée?¶‚O=ˆo­ª•óÁÓÕ #.Ы֑âG»;[\0ߥˆˆˆˆˆ#R0”RËð©Fý ¬ßº†qI¤f“›ª5cèíÝèZ·Œž—AÁPJ1ã]oF×ë]ˆˆˆˆˆÈ NéDDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœÓõ.à’Øöóë_ñgœ,~´ø îkàQÀªöcËxûýÅ6êpßëÐÁ« µDDDD.™vœM¯då–}>™@ŠÍ‚‹‡7å+Q«n}Z´hH-?gÀ$9b.þ°äÊ¡Œ|´3U­×»z‘’ ÏggíÌÅ´ª1€w…>¹¶ìq[ù~Òo¬=™…™»ÔFZâi'žæðÞ÷¨Á¨ö>˜ÄìÛ˱” ìû÷p ¹U½uý""×_Ɇ˜ØO…óëMxaP0®×»qf*[çÏaíÉ,p­D‡AèÕ¨^F*ñ±q;¸›Í;³hÚÔ;§g“…*í{Ó'e;É­h®žL"rƒ(ÙÁÐZ‘&õ³Ø¾ýÇWÎaa³ÇXÍùzW%"""ŽÂ~”{’1±P±ã`înW•ìž¡n¸{úQ©j šuÊ»‰5 >ý)\Ɇ¦A•.}ñŽþž•±ÇY2mÍFw£jß•™ËîMߺ—È#1œNÊgüƒ¹©ýÍôjˆÇy7òìÑKx󃥜¨Þ—×kDÌŠ¥, ßŘl.ÞÖjLhŸPZUr&ùÐz.]ÏæÈãÄ¥xÑ uWu­ïCþdqjçj.ÿ‡ˆƒ1$dZq÷«HÆ­èÚœjet7QDDäÆd#Ë`àêæZàxy™Ä­üЧíÃæÓ†1¯ ¢ž̸5üï9ì¶²™Q†ö¼È°s$šÉÑ„-[Ί-û9›ŠéêEùªµiÝ¥+]ëû£[å"R%;’A†slÌÖo7s&j9¿,oÈÓ¡(ÊsÜi{–òõ¯IÀÀÉ݇²å<ÉHˆåäþ­üq`{Ï<ÂS¡•.ølÇvóûkX·9ÓÉ7+¤§ÆqhëßL>Cb_VNãx–w¬ö4Nìgí¼ÃL~˜T?×åÕLcÿ¢oùdáA’1pñ  ‚¯ø“‡Ù¸ì0[·äÁ‘Chæ«p(""rñ”£rE+F¢¨u+ÙÚz0M}.aÐw'O*T "-_0´%Ÿ&:6 ÓpÁÕåÜr{ì¾›8•u§²Àꎹr8§Ært÷zfîÞÁö~02´²±‘"+áÁÐFVx5¹…A ö0e{ ûÏáï&ѵܿÿRv¯ß‰¡½*âT¿ MªúàlY±„ÿò%“×DZÿ¯Õìì<”Fùn¹™©{YQ‰NÃÆ0¸iÜ Žo˜É'?m&&qS1ðéÅ“w¶§Ž¯3fÒA~ûs÷¦r|Í"zV§™[ö¾Ò#3ùC$ãIƒÃxèæª”1À¿‹©ŸÿÀò£øiVmjßß=† ""rƒ1|iÚœeûɉÙÀçïFÓ¢óÍôèЀªžEnÔðjÌ=O6λ0ó(¿OøŒèXŸ&½èU#gf,+™ÉºS6œÛðàÃýhàfQÿÂøY»Øóût×{œ~•5䩈MÉžÇд‘e _Ú êN]73m?s§¯ç”½Û[+Òæ–N´¨– œüiѹ å-`¦œàh\A;²àߦ?·5«€»0\¨Ø¢'7gœ†K-úßÙ™º¾Î€Å³:=z6Á×fú ¢NçìÓL柿7c×z=– ¬¾õÔ· ^†IÒö lI4 ¨CDDD®/ƒ2!ýuokª—10“¾à'þûê{¼ÿóßl9™Á¥Á3Ø¿h g`øÝÄ]C›p¶ó=*œe{Ó0-þt¾-'nTéÔîU­˜¶¬ ¢°ž©""ù•ì`œýmk)ךۻWÁÅ0IÙµˆ©Îd¿dEèïo'3%‘ØØXbN&&ì hf’‘YÀê†+µëUÉÛwßð¢œN¬T›ºù†žvòÀÏH'--§hÛvïKÇÄBÕúµñÉW¨kÕjYÁÌ:ÁáãEIº"""rí9Q¾Ù žéqê}5|œ ó ‘a øôÝÿ1qé!’‹•MÒ"3eÙ1²Œ:ÜÞ¦¹Ý†LNïÛÏ);žµhX-_ç/‹ÁÕ¼°`'>úº¯,"EU»’žÏJP—þ„nüœ…G“Ù2÷w6Ô»“NN…ظ’¹ ×°ãtÛæMá;¯'øOkß"ܬ3e/3~YÃq»… ‡0$Äã¼íìÄÇ%`Ì„uŒ³®Ðýéi¤™P¤ƒŠˆÃ+EÁp®B¯!­Ù8i '¶1cnSêõq.pT®¬c+™0n{SÁ% &í;Ö#¸œ7îÎˆÛÆÌ9[8Uè¬X/Òeß0 Œ"ýæÏ ž†ïÀšT)t‚[*_4EDDä†dõ"¸UožhX—©ã¿áÏcÉD¬ÙJL«N”ÿ·Ó¹™Ä–™3YyÊŽS¥Î ëS÷|Û˜öì›Å†[YjV÷ǵ}ZÊùã¢Ë)¢Ò ·Z¡ÜÚ*‚OÃâ‰Û0ÙõÚátÁ/Å,v/ÿ›ÈT‹ÏM<øÔíÜt^ð²Gç÷kP«áî…‹)ÛßμtSODD¤”0<‚騬Ë?Š=>žx;”¿èX0&ñÿÌåç q˜Î•éuw(5/VÔÀÛ× ƒxp¯ÃÀ¨£ñeDä (ùÏægxШoošy˜ö8VÏ]MtþÇóÌ4bãÒ0kõêçi3I;q‚ØkÑsÓ© u«;c`ãÀ–í׿˜"""rdrút"vÀ(S†›–ØŒÝÈO3¶oºR³×­ÜR¥ >OÊÕ Æßö3»Ù¸?ãj."¨ôCÀðnÌ>u)c˜ØããˆÏ 7|}²'¡µ<ÊÑó˜ÉŠ‹`ú¤]‹fxÑüææ”µ@úž?ølêF%ž?Ì´¥p"r3†&í”#"""Åc?¹ŠOÇÿÄ´?ÿ!âP ñ)dee’{„ æ§õI˜†•›6 ÒÅ®ºì§YñëïlMÚ=¹¯kÅB»uY«µ!´–†=–¿ø‰…;Osn(“ô3Ñl]»–­E¢]D$[©ëJšÍ  u?ú¬?À´Èôq¢NÛTÚ¸‚£ÇW0îýhÕðÚ|‚=»¢H¯BïìNºú•ºÕ½…‡ûÆ0qþ>¯žÊÛkgáîé+™¤$¥’n7qª7˜-«â¦~¦"""73‹øƒ[Ù¼o+K zݰâß /÷ß\ñ"wãM6ÌgÖîLç7ðÍGó­c¥r—{¸¯¥†%€Îw%곩¬>¾‹YŸíf®«^îNØÒ“INËÂnøÑõñV4.{%߬ˆ”f¥4–²tÒ™ðÿ-á`æ…ÑеF/F?R†Y ÃÙvxN9QÆ¿!¡÷2 k-ŽM{—=ë¯E¡®T}Wjndéߛغï1‰ ¤Δñ­DÍÚõhÖªî¿v?‘kÏâ߈“ Û±ŸCÇb‰KJ#ÓnÅÕÓ‡ŠUjиe;n¾©=›¤$$æ´úÙI9}”C§ó¯cÅš˜uî¸~¸÷©Š4^¹’•›#9x<ž3gLœÜÊP¾z5ê7nJ‡ÀRÙ1LD®Ã4/˜¤¡P[v I½à«YˆˆˆˆˆˆÓåf5ÝJqp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qpNÅÝ`÷è«Q‡ˆˆˆˆˆˆ\'ņuƒƒ®F""""""r‰¶ì:pYÛ«+©ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ9l0 [·ž°uë¯w""""""×Óõ.àZ›>s6o¼ù6 ‰‰T âå—ž§g÷Ðë\™ˆˆˆˆˆÈõá0-†kùãîa<ýìØÜPp$:š>Îw#bÇÎëX¡ˆˆˆˆˆÈõQêƒaÔ‘hž~öî¼gaëÂhݪ%¿ü8…_~œBP` aëÂéÓ0o¼õ ׳d‘kÊ0MÓ,êÊ[v I½à«YÏ“Àäï~`ò·Sr[ƒ=j$·”gÝo¾Â¸ñILJÀÛˋѣFòÀý÷]óºEDDDDDŠër³Z© †‹–,åÍ·ÞåHt4^žž<0|¿ooï·IHHàãñùvʹË*ñÁ{oÓ¶M«kR·ˆˆˆˆˆÈ¥P0ƇŸ|AJjê5®êú(Ñ-†ÓfÌâ™ç^Èý÷ða÷2fÔÈ+Þ¢—Àëo½ÃŒ™³s—}ñÙ'šûPDDDD¤û¿'ž [çö4mÜ wùéØ8¾ûiO=þÔ­]óºÔWÝb8}Ƭ<ÿöññ¹jÇ20.zl)™š6n@ÝÚ5sÿ´kÝâz—tÍ9]ﮤq&2ùÛ)¼òò <ðŠìsòwß3nüÄÜ)/ÎJHH,d ¹Ñ¤¤¦r$úX¯‰>†aEz-Àß¿«RãõT*‚áða÷’˜ÈŒ™³IHLäégÇ2}Æ,F=þØ%?g¸6,œgž{!wÊ‹³Ç [ÎÎ]»¯Té…Ê\ñ­šA¬píÎ'&ÐÏý*ÔLaÏ‚¯˜øËŸlØELbk4¤ß˜ÿ2¦ƒÿwR÷3û1¼8=’2w~Íê7Úâ\ÔM7Ì໾b§½OÍÆãu¬ç½šÉÉÍ ùù·ßùs]ûŽÇ“n¸ã[©7uìǃÜA› E¹¬ËdÅ ]¸j,v¬Ô9ßG‡\ü‚0åožéö¦ÅØÁ)ä‚ڒ猤ÙSËHÏ]b`X¬¸yúR>¨&Zu¢ß·Ò£¶—®#äºúöÇ©lÙ¶£À×~›9¯Ðíò¿æîîÆø÷^¿¢µÝJE0ôööæÕ—_`èàAŒ›0‘uáë [Nغp†Ĩ'+ÖtÏ<÷Bž)/Z·jɇï¿C•ÊAÜ~WiôÞäô—¸{ÌBbìç-NŒáÀö#àïdî[Ä׿ï%÷·ÝÓŽJ%º3²ˆˆÈ•“ydïŒzï¶$`ÇB™âlœº…O^úŽ]æ…Wgf¾qÿIŠáAÅÆ´o\[<6‡³ìÇ·ùkq8ïýö1C*[ Üý ;‘óæ±å±šš^MÎü=‡%§í…­pv‡8UjD—å²ÃŸ=ƒäøãìßµßw®ã÷_§2ôßó^Ïò%û9&)ÑÊ^¡V¾¿Ò×Z¥$žÕ¶M+Ú¶ùži3f1nüD¢eúÌY,^²”†»èT Œ›0‰Éß}Ÿ»,(0ßÇA&¸OaÍ‚åœÊù½o”©A—¾vNæ”ÙœAu­@*k¾y›¦žÄ¨þ îR0'VM≧¾$<¹*ýîlÊÚßVcû4þ™ô*_ï¯F“úÑlÙ“ïeצ]ÚqStg~ˆõ}ÉG˜ûüýŒ™·Œw&¬ ×û7-ZkÒ¢a ·-dÖú'hÞέàõÌX–ÌZA‚k3ÚÔÙFXÁƒ7àÖjŸ~Ô—ó7O=Ä‚ÿŽäÉß"™ùÞ·ÜÑí9š—ª«O)InÒŸÛ‡ô¿ÞeܰJåeý­C±pþ¬Ü©&7a"½û .pª‰é3gÓ¡shn(ôòôdÔã±zŲ&^õ®f2gldQk¥âàWùò¿cyåµ·˜ðú ‚­`&¬dÆâSüÛ=CGbžœË O|γO|ýÃVŒíSþù”±“#);à1n«ZÐYÖÀ¿óX~üòiŸ \*ÓçÿQÇb'~ó&"‹z`ÓÊMÝ»RÎ<ÉsÂH.d5û±EÌX“Šg‡P:”)þÕˆá^ÞÏŒ ‹»íØv¶žÐU„ȪTCÈî^:fÔHÌŸM÷Ю‰ŽfÄ£sÇÝÈر“µaáôî7ˆ§Ÿ›;¸ÌÁY½bcF¼žåçaX]pNßÇœ·GЫ] j‡4£Yè=<ùù*Žfæ]7sÅK4«BõZ éúÁV²R0ÿ½G¹¥ÐíL’÷-câÓñqøÙ…6Žý0ŒÚµB¨^« ÷ür‚˜?óì}¯òû™ì_趃ß0¨^Õk…P½N^ËÌ{üÚ-ùG2ØNþý«ÜÕ³ ê5¢Û‡ÛÈ cç2éµÑÜÙ¿Í›ÞD­ºMhÐ2”[î{Žçí"¡À‰Tì$ìúƒO^Aÿ®hÒZ ZÒ2ôv†??©›OsþfY§¶ðÛûOr[ÏN4¬ßÚÚҾ߃<=q{’Šyi {úðêSmpÍ0)öÙÉjÁjV'ŠØ‘ÈÀÚ¬?½ b—Íå¯3ÕÆÁùsØáK·mqϸ´ó¦á@@0)ú$i"WÏÒ嫸gëEš¿ uä(Ë–¯*Õs–úÆü*•ƒøêóI¬ ç·Þfç®Ý„­ §OÿÁyÖkݪ%¯¼4–õC®S¥…3\Ž3õÑ{øk}|nk]ìÁÌüèÖF¼Ãôñýº âÛ8ü÷O¼¾e%?…Å]d;;~}‡çD~w3k o=ðs’‹ñÛÜLeÛÆUÌÿûcžœ~ˆìs‰Þ¾>€™°ŠËÌSyïfÅE³sM4;×.fÁ¦Oøí•øçÞ ÌdÿôgöÊ"¢òœœ’ˆ9¸•¿Eb»é†6ÍnaM‰˜Âˆ‡>`UŒí¼“l<Ñ;×0}çZÌÌGß½Î-•Š~¹Õ{ÝIõÜ9²qâk|éÏÀ‰ÏÑ#Àdº½¸É)•ís±Ûf¥|«ÖÔ*ò)ÍF–µ)ûVã‡/V0{Y,}äí¡dÛËœ9ØÊ apG7"¾º´Tg;¼ˆ8;Ö &4­XjÛ$¤„ؽwSs“÷Þkx¸mtÇßfÎeOäL ´K‡«XáõSêƒáYmÛ´báüÙL›1‹7Þ|›Ä¤$ û9ÂW^{COVoßÈŸ½îЗV•lì_óë£Ó0MǽË{‹;1¾—ÏÝM³vÍåGKÎv&‡Âþ"ìpJ¾í¼¨Öóq^«rŒu?Ldþ`Á¿Ý0ï„ -á6öj­ù‰ñ ’XÚòàºQÙÔ Î&²qø×±<“–š V‹þþ¾Xû3ÝU‡yŸ¤VÇî„¶ªK%ö,ø× ËL#ò—øþö¶Œ®—½ïÌ_ñÄ«gC¡“_mÚwl€úIöïØJDbnëY.»H§DDäKÙ8‰¾ÝGÙãy1Ôƒ¸"·¨Ù29¹ þú)ÿûe/®õïãÝQ­)äIÁda³9Ѱ_j=‘Õ³s|àyÆÈÜ<‡9‘Pù¾þ´v³±­8½@MɱÜú'ß¾÷%Û¬Õ¹mìpš:Ì•§”QGŽyâú=‘HIQ‹a©qëAT âÎ{†”ŒÁe gBÌÌ1 q̸å<7h$SØÀDz9+8Ó³¾ù“aþí×ñúàùî@ÞíÊ·À½ÍNÀ’Os‚¡wýÜ{oÓs? õjP+k)ÌÞµW}zß}77]ä'ÈLM%Ý«>w<ó #z7§º¯»Íš ÄG¼qIDAT`>‘Åw”%¸¼ë¹úW#þæQÌŽ³cfígÓÖxÌz¤±ê‡ŸÙ‘ž}Æ´”ïɇ3>d`nk_:§Ž'ãã›ÝU%nɦÈÂ,¾Ýxó»÷X6ûȽÛûqºçK,K´sfåt~?ÚŸ*+ŠˆÈ5”²‰ñ/OdÙþ|>¶kvï˜ …™¬z¥÷ý“ÓÈÀê[‡Þc¾àùárý—Íó1k­> lø9ïn˜Çü¨Ûy¸ÚÙóaa³ÿ Šê<: ).ü—Ðj’4ç1êÌÉ·Øp£j§{÷ÞÃô«§é*äú+j,L•ÊW¨’®†Kkúôɽ høuäž¾ÕsB›IÚ®ˆ‚6Ï¿W níSãß·»R,þôzãKÞ¹« Á¾ÎX±ž×°hxå …îU¨^ñìiÃ$919ûÞ™ï´añ£¬¿Rl€Cûc÷à`Ë Ê`!;^ýƳîãîx\õw&""rq™f1c&Yöé o1½µvòQï†|„3ÍÆÎgúƒU/ά•¨䄱%‰˜S黌áK×7ã·x> æ¬ç© ‘Ì\—ŽW—~ô,w%n!;S¹j,œ"æ„æC–GTôÑ"­·{ïþ«\ÉA-†%m?ó§­Ï×Ï~r)ßÍ;;½„[HjÔ¯2ßvfü~ûýpÎ:ú¨]ä[ÎNN¹-ŒfRq—ð(€ýh4ÑgÏ N|_›ìP˜IˆV.ØwÞ*ÞçþîÞ”Ñ_!êá7ùýp:Y±Ìÿ:‚ùùjq­Q¬@DDäÚ³dÞ£YðŠjÖ£npEüÝ-¤Çc÷–­ìËÂЊ§>IËK¾nu¡YÿÞÿð%‘6+U{÷§u1ÇØH[ÿ5œ“Ó"h’•–À‰ÈìˆN$Ëð¤ÑƒoñdÛÒ}a-%ùg ÓŠ´þéØìÁgÜKñÀ3PƒáÃïc]øzvìÜÅ÷ £G÷n¼üâXªTº¢Ç‰:Í3Ͻ@غðsǾØ=F¡ 'jÞ÷1ÿm¸•~^ÄÚ]G9“éŒoµÆt»õÆÜß&Ïd´ç3mõxü§Wˆÿ1S–n':ÙŠoÕÆô¸k$cîiN¹âöåp»‰§¾ž€Û‡_1gÍNŽœÉÂÙ»<Á ZÒ­žGѲ±ÖäϾÁcü'LY´‘ÈÓé8yU fÓnÜ󸏵âìþ*§äÿ,üéøüÌoû+_ÿ¼U[ös4.…,§2”«BÛ^·ñpËswJ]ká“y71à発8Œ-{£9’…áâI@P µèÈ-··£tÿ‘Ϲ5½7ÿ…+Yq€Ë·’šÎ¾T nûûpϽ}iP”i% çÔ /ýëLæ{ªÑ@Ób>©h’uôå>²e`qrÅ3 ˆ¦=Ñ÷Îû¸³CÅœjQäªð÷Ëý{Jjj‘G­Tzž0ÌüÃ[^Ä–]hR/øjÖSl‹–,å7ß!ú蹇GG?1’î¿ooï·Yž;Áý/?N)´¥1!!q&1ù»ïs—òáûï\õÖÉK•¹â%Z?4ƒX;àÚO6L Ÿ’ˆˆˆˆÝ5tìkïP%¨Ò¿¶î‰Ì|æþ»o¥]ëW½¾Ku¹Y­D·ôìJÏî¡|<~"“¿BbRã&LdúŒYŒ5’¡ƒ^Ò~'÷=ãÆO$!1/OOFɃïQK¡ˆˆˆˆˆ\qþ~4iTŸ-Ûvù9C?_š6np•+»¾J|0c£¬È-|kÃÂyæ¹8»lø°{3jd¡-"""""Rr<öð0–._Åî½ûþu>Ã:µjzs‡R=¹=”¢`P¥r½ÿCgÄuáë [Nغp†Ĩ'+ôùè#ѼùßwX¼dYî²Ö­Zòáûï\ñgEDDDDäú íÒÐ.®w7ŒR9•LÛ6­øíçïùཷñòÌysúÌYôé7ˆq&åY7!1û9ÂŽ]BsCaP` _|ö ¿ýü½B¡ˆˆˆˆˆ”z%~ð™“À7ß~ÏøOÎBo/¯ÜgÏçåéÉÇ1fÔÈkY¢ˆˆˆˆˆÈe¹Ü¬V*[ Ïçíí͘Q#Y¹|)ÝC» ‡ ÈêË EDDDDÄá”ú`xV•ÊA|õù$~ùq !õêæ.oݪ%¿ü8…ÞGƒËˆˆˆˆˆˆC*UƒÏEÛ6­X86;vР~Èu®HDDDDDäúr¸`x–¡ˆˆˆˆˆH6‡éJ*""""""S0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""®XÁÐÃÍ•¤”4LÓ¼ZõˆˆˆˆˆˆH™¦IRJn®—µŸâCwWRRÒ°Û EDDDDD®7»Ý$%% ÷k ½==HJM#3+ë²*""""""—/3+‹¤Ô4¼==.k?Å †n.NX ‰ÉiØlöË:°ˆˆˆˆˆˆ\:›ÍNbr›‹Óeí«XÁÐ0 ü}=INM#5=C]JEDDDDD®»Ý$5=ƒäÔ4ü}=1 ã²öW¬`h±Xp¶Zðôp#6>”´tµŠˆˆˆˆˆ\C6›”´tbãðôpÃÙjÁb¹¼ ' ³˜CŒfffb·ÛILI')%2înx•qÃÙÉ ‹Å¸ì¤*""""""y™¦‰Ýn’™•EbrÉ©ixz¸áåášÝ€çì|Yû/v0HOOÏ.Êf'6> »iÇÓÝ 7<=Ü.« É+)%””4’R³Ÿ)ô÷õÌi)4pu½¼Iსišddd?chµZHÏ´‘”BJj:)ié—]”ˆˆˆˆˆˆœãáæŠ‡»+Þž¸:[±ÙìX,...W¤×æ%ó233Éʲ]v""""""RtNNÖËî>z¾Ë †v»»ÝŽÍfÃ4M.oo""""""’ŸadÏaµZ±X.°™ ö¹ÁPDDDDDDJ¶+3EDDDDD¤ÄQ0qp †""""""NÁPDDDDDÄÁ)Šˆˆˆˆˆ88C§`("""""âà EDDDDDœ‚¡ˆˆˆˆˆˆƒS0qp †""""""îÿÙ^ïã3·eIEND®B`‚bpftrace-0.23.2/images/ci_job_env.png000066400000000000000000002452351477746507000174700ustar00rootroot00000000000000‰PNG  IHDRØÔ° ,asBIT|dˆtEXtSoftwaregnome-screenshotï¿>-tEXtCreation TimeMon 21 Aug 2023 01:45:42 PM MDTÏ›ï IDATxœìÝw|ÓÕþÇñwÒ–Ò½[F›e+Ãâ@½n¢(¸½†pëU¯çu ‚ˆ KöP6BYe–QJ7ÉïÒÐК6iмžGòç|ò=‰ôí9ÇЬu‹T‹ÑÕç6Àl€Ø;°v `ì@ÀØ€ °`6ÀîŽhÄ`4ÊÃÃCîîrs3Ê` ·À?S­6//o5ðôtD-@½Wã€ÍÍÍMÞÞ>2º¹9² ^«QÀæææ&Ÿ†& G×Ôk5Ú,ÍÛÛ‡p ç%»6//o–…ÀyË®€Í`4ª§§³jê=»6gÕœì ØÜÝ Øp~³+`ss³{Ë6àž=Ø l8¿‘˜v `ì@ÀØ€ °`6Àl€Ü]]À?‘¯É¤^½z¨O¯2™LjÖ,^¾&“²sr´cÇNåääè%Ë´dÉ2eç为\ØÁЬuKu/öópf-ç¼ÈˆÝ~Û-téÅÕ¾gíºõš0éU¥:äÄÊà(l2jä]7tHïÿ~ê4}úÙ—Ìh¨çØjÉ×dÒ„Æ©cb‡Z·•¼c§F~”  3ºº€s™¯É¤·Þ|Õ!áš$µh¯ï¿ýR‘iŽGÀV ^¯ÍâÚ¦¯É¤_'_“É¡íÀ1ØjhÔÈê˜ØÞ)m·h¯;n»Å)m×GC‡\­ßgÏÐí·Þls<((HKÎÕ’…sÕ´Ic»Ûí˜ØAKÎÕósT©.Wö<Ú·Kpu)œ·Ü]]À¹(2"B× âÔ>®:D,Y¦µëÖ;µIqÏ]~Ó° Ç •“›«ÔÔCÚ¼åoÍ™;O[·%;¼ÿž=’äåå¥ ûôÖ§Ÿåðö‰l5PW³Ëêz›ÅbQQQ‘õŸ‡‡‡‚Õ¶Mk]?tˆ>þà]Ý{Ïï÷ç³´;%ES¦ýèð¶! MŸúõ€«K‘$5j­% çꢾ}\] 5 6;ùšLºìÒ‹ÒÖäϾÐÚutýÐ!êÝ«G…ó;(2"B©‡9¤¿³Ù´y‹FŒms,00@ mÚèö[oV‹ÍuóM7hé²Ú¸i³Ãú]°è-Xô‡ÃÚƒ­6­[)4$ÄÕeX]ا·«K V˜Áf§Ê‚°š˜0éMþìK­]·^ßOæôþjêøñ -^ºL£}Bùù’¤ÎttiM°Ï%¸º+wõ»¨¯«Ë V\6ƒ­m›V?öOÏÐ˯½¡]»STRRrÖû ƒÂÃÂôèãԦu+=÷ÂD­Xµº*.Õ»WÏZ·1aÒ+š5{ŽõýõgØÏ­w¯žg àêJVV¶ÒÒÒÔ¨Q´<<Øœ»õ–áºûÎÛ´åï­ºgDåËÆÅÆè‹O?’$]zÅ5ÊÉɱž{ìáQºêÊ+4oþB}îE»êjܸ‘þ5üFuîÜIÊÉÉÕê5jòç_ª¸¸Ø®¶Ê uëÚEõí­víÚ*,4LnnF¥Kןþ¥Ï¿úZ©©g~ýåd5iÜX/»Ržž tËðÕ¥óŠŠŒ”Ù\¢”={õëosõÓÏ3d6[*íÛd2iø×«oŸ^ŠŒˆPQQ‘þÞš¬/ÿ÷Öüù—JJJäææV­Ïѵ˺ÿÞ{'IrÍ•rÍ•Öóÿûú;½÷áÇ6÷DFFhØuת[—Î Un^žvíÚ­Ÿfü¢ +ŸeؼY¼†¹ZmÛ¶QxX¨JJJt4-M;wíÖ¢EK´dÙr麡×è–›nPPP$éùñÏÚ´óÈãOieþ–¨)—l‰:ÈÏ×Wáá7æi~‚vîÚ}Æ­,\ó̿նM+IRBB›: ØL&S­î?=\{úÉÇÎ8K-""¬Vý9Š¿ŸŸ"##$I›·lqq5¥’ºuÕ‹Ï‘§§§,‹ŽK—»»»è§¾}zé÷>¨Q»ƒ¯¤Ç)]*³¨¨HGŽ•·——"#ÂuÅå—©oŸÞºëÿî׃+½¿s§ŽzpäEFF¨°°P'òóåïç§6­[©MëVJlß®Ò 122BÿyýësÎÊÊVaa¡:&¶×õɧŸëøñ …„WësÄÅÆ*;'G™ ðWZÚ1í?pÀzþ@jªÍõݺvÑóãž‘Ìf‹ÒÒÒäÓÐG/è¤ÎtÒÜßçëù “lÂÁ«¯¼B<ô  ƒ u,ý¸|}MЉQlLŒzvOÒ5CoTQQ‘Zµh¡½ûöËÏÏOîîîJIÙ£ŒÌLk[ÙÙÙÕú\¸šË¶ßç-P¿‹úªY|œš6i¬±Ï>uÆ­²pmó–¿5ó—_ë´îæÍâÏx~íº jÞ,^&Sà ç* ×ζŸ[dDDÍ u7775o¯GŽ›››–-_¡eËWº´&I Ö¸1OÉÓÓS7mÖ ^¶^ÑQQzxôH=<ªòÙtg3þB%´m£yóè¯uëTTT:.>>N¯L|Aaa¡ºõ_Ã5aÒ+•ÞÿܸguàÀõ°6lÜ$³Ù"??_ø¿»5øòËÔ¿ß…šöãÏZ¿a£õƒÁ ñcžVdd„ŽK×ø&꯵ë$I¾¾&Ý|Ó ºóö[íúß~?Uß~?UãÇ<­þý.Ô‹—êõ·Þ®ôÚȈpkXùéç_éß|«üü õéÝSO=ñ˜è§äí;ôÍwS$IÞÞÞzàþ{e0ôáÇ“õí÷?¨°°P’äïﯤn]äå奜Ü\IÒó^’$ý<ý{ê“O¿`ï=À9Ée{°>rDϽ0Q»Nje![|\l…%ðª מ{a¢9Z§uWœ•™üÙz`ô#ºî†›µ}Ç.›s5 ×êZ›Ö­ôËÏ?XÿÍž9] æÎÒǼ+www½ñÖ;ú÷3ãd±T¾¼a]rõU25l¨üü=ùÔ›ÙdÔ¿Ÿ§#GköÝÈÉÍÕ„I¯håê5ÖpM’vîÜ¥/¾úZ’Ô±Cû3¶ñØ“OkÝúÖÙ^YYÙzõõ·¬uvëÚÅæúÎtR›Ö¥ßí‰/¿f ×$);;Gï}ð±.Z\£ÏS÷Üu‡¼¼¼ôãÏ3õɧŸ[÷Û³X,Zôǽr)É®*£±ô?‘áòôôTqq±¾úú[k¸&I™™™úmÎïúéç™N«WqYÀ&I{öîÓ¸ç'œ1d;[¸V×aONNn•ç:,IÊÎÉу£±†lçB¸&•ÎVó÷ó³þ3™LÖ0%<,L11Mäâ*Kõì‘$Iš·`¡2³²*œ/,,ÔܹóÞïž½û$IU^³jõ¥žü.”WRR¢M›ÿ–$…‡Û.ýYöyL­r²Nš­Ù AõêÙ½´™³*½fÉÒå’¤àà ÅÆÆH’ÒŽSII‰ÜÝÝuõUƒRõ‘Ë–ˆ,S²{ö)ÅÅÅÚ,¹kwŠB‚ƒëM¸&IÛwìTÇÄÊg/=0r„vìØ©ä;­![bb-^²Ôz½áÚ™=GÛ¸i³FŒm}ïîî.???µmÓZ7ß4L×\5Xú]¤û|H»v§ÔY]§3 Ši*IÚPn™ÅÓmÝ–\‹>Œj—ÐVÍâã$//O FJR…Y–åmKÞ^å¹ãéÇ%I xØ‹‘$mظ©Êïum>Ï™ÄÄ4•···$éÉÇ©´ƒÁ`}¬;w)++[S~˜®®ª‡G= KД¦kÑKTTTä”Z¨\°IU‡l¯¿ù¶î¾ó6µnÕR’ëÃ5IÊÉÉ©òœ¯É¤·Þ|U£F?j Ùj®I¥ž«+==]‹—,ÕŠ•+õÁ»ÿQ‹Íõðè5rÔÃ.«Ëǧ¡uf]ZÚ±*¯;ž‘Q£ö/¹x€î¿÷nÕl¶^ffæY¯1È`óÞÏÏOR鬰3µk6[d4ª¼¦&‚O††’Ô¢y³³^ß AëëwßûPSSuÇ­ÿRÛ6­Õ¶Mkeffjæ/³õ픩:~¼fc@}V/6©òí­×_–Tº´^}×$iñ’¥êÝ«G•çOÙÊÔtYÈò+ë癳ôèã”Ø¡L *'·ú³ëÜÝ÷U+7™JùU^WRRbwÛ—\<@Ï>õ„$é—Y³5sÖl¥ìÙ£ÜÜ\™ÍuLì ·ß|õŒmԤ߲T¶÷YUÌæŽýÙ–ÍÆ³X,êÛÿR™Íæjßk±X4múÏúeÖoêQ_ ¾bÚ%´Õð›†éš«ëåWßÔïó8´^\Í¥{°îô=Ù¤ú®IÒâ%ËÎzMYÈÖ1±ƒ|M&=U‹=×Ö­[_£ûœ!ýøqë뀻î-›¡å¹¹y2›-g­£aÆv·}Çm·H’æÌ§‰/¿¦›6+;;ÇÚŸ‡‡s2éì“3#Ïôy<==T–9v,]Ré2á5j£  @³fÏш‘£uÇ=÷iíºõòññјgžT|\¬#ËÀåêUÀ& ÙvìÜ¥ÜÜ\mÚ\Â5©4Y»nÃY¯ó5™ôö›¯ê×™Ó5¨†áÚÚulfÁ¹Z£èhë댌SË æççK’ü«‡Z¶lî°:Ìf³öîÝ+IjÓºU•וíkV]^^^ŠŽŠ’$-XøG¥×”w´Ý'÷´säç)cQéïÆPÅÊ’{÷íSaa¡$©[×.5꣼ääíýÈJÞ¾CF£Q½zv?­ “¿cÇ®t @©w›T²=õÌ8½ÿá'3þùz®•™0é•:égòg_ÔI?Õááá¡+_.IJÙ³×fyÈ£ii’¤ðð0………V¸×ÍÍMW\v©CëY¾r•$éÒ‹ÊÛÛ»Ây£Ñ¨K/hW›EEEÖ™jžžžλ»»kðƒjPíÙ-_QúyZ·j©V-[TzÍ Ë.©QÛÙÙ¥³ãÂÂÂ*=Ÿ››§eËWJ’n¹éùùùÖ¨ŸòJJJtøÈ‘JÏe¬'¼Šz¨ïêeÀ&I‡ÑôŸfèØ±ôz®IRê¡Cš2ušSûX»nƒÖÖƒå!=<<Ô¾]‚^™ô‚E—ÎÞúêëom®Yóç_*..–›››žù÷ã6![£FÑšôâsŠ.7ûÍ~˜ö“òóó¤‰/ŽWDø©¥ ÃBC5ö™+¦i»Ú,))ÑÖmÛ$I· ¿Aa¡§>G|\¬^{y¢š4nä˜pš«VkÇÎ]2 zá¹1j—ÐÖzÎÇÇG· ¿QW ¾ÂÚcçÎ]’¤n];+±C{ëñ X_¿ÿÑ':qâ„ÂÂBõßÿ¼¡n]:[÷f3ŠÓ·ßªQÜg½§Q£h=þÈhuhßÎféJ£Ñ¨‹öWR×®’¤¿ÖÚ~Ëê¹æêÁ6ß»?®àœ ¥Î“?ûR‰‰‰jÞ,Îámoß±KO=3ÖáížMÛ6môûìÖ÷ƒÁf&—ÙlÑ—ÿûZ³›ks_VV¶>øh²îq:uLÔ´ï¿Öñãrww—¯¯If³Y“^~UO<ö°5´©­C‡ëù /iܳO«s§Žšòí—J;vLnF£‚‚‚TPP 'Ÿ£—'½`W»~ü©^e¢âãã4åÛ/•šzH~~~òóóUvvŽî{à!½óÖkƒÌf³ÆŒ{Ao½ñ²"ÂÃõÞ;o*3+K  ’›››Þ}ïC ì‘Z´°o¹Íßç-Ð-ÃoTxx˜Þyë59zTînnZ±ju6æþýôô˜ñ¥ÁdLS½öÊD•””èD~¾|¼½e4–fñËV¬´¶Ûà䬯+_®¢¢">rTfs‰B‚ƒåãã#Iš:íGmÜ´Ù¦žï¦ü ¾}z):*JS¾ùRGަÉ×ÔPÿyç=Íš=§6€:Qog°ÕwÙ99zpô#ÊÉÉ=ûÅvÈÉÉÕÄI/+;'Ç¡íV‡Ñh———õŸ§§§òòò´oß~ýôóLÝ3b¤>úä³Jïýæ»)ú÷ÓcµjõŸÊÌÌ”ÉdR^^ž~Ÿ·@wß;R³fÏуZï¢?–èÎ{Fhîïó•ž~\A’Á _gÏÑ¿n¿GËV¬´._Y]kþüK#G=¢•«V+/ï„"##TTT¤¹¿Ï×Ý÷Þ¯äí;”¼Ý9ûâíÝ·O·Ýy¯¾ýnªö8(o5ôi¨?ÿZ§‘£Ñ7ßMQÊž½v·›“›«‘£Ѽù •™™© À@—”Xg’•YµúOÝrû=úäÓÏ•œ¼]¹¹yòñöVÞ‰JNޮϾøJïþ÷ëõûöï×ó^Ò²+•™™¥ð°0EFD('7W‹þX¢Çž|ZoþçÝ õlù{«zôI­[¿A ÐñãJ=tØþ‡€ šµîPí5çüüœYË9)2"B^ï™lÛwìÒÄI/+y‡sÔž[Phĸê^ìéååÄRÎM999š?¡<4PÛ6­kÜÎÚuôèãÿf@=Ç 6ê˜ØAwÜö/uLl_í{Ö®Û ÉŸ}¡µëÖ;±28 ›DFD¨w¯êÝ«§"#Ân=—““«í;vjñ’¥Z·n=ËAœcØ;]]p.!`ì@ÀØ€ °`6Àl€ì Ø,³³êÎ vl%%l8¿Ù°9«àœ`WÀVTDÀ€ó›}{°™Í*,(pV-@½gWÀ&Iùù'd.)qF-@½gwÀ&I'NäÉb±8º Þ«QÀVRR¢¼Üf²à¼S£€M* Ùrr²Ù“ ç÷Ú6ŸB…òð𻻇ÜÜŒ2jœÛõZ­6I²˜Í*,(`6þñ˜jØ€ °`6Àl€Ø;°v `ì@ÀØ€ °`6Àl€Ø;°v `ì@ÀØ€ °`6Àl€Ø;°v `ì@ÀØ€ °`6Àl€Ø;°vpwuÿd¾~~ ‹PPp°ƒ‚äåå-Iúí×™*ÈÏwqu¨ 6'jÖ¼¥7iêê2êL«Ö­õ‹lŽ•””(33S;¶o×ì_Õ† ë]T]ý`2™ôþ‡Él6ëÿî¹['òò\]’¾ùî{yxxhèkÚîeƒéλîÖÓ§é«/¿thÛåÅÄÆêÕ×^×Ö­ë™§žªu{Õ£-Zh¤—´yóf}ö™Z÷[^^^úêëo*¿õ–›•››[guôéÛWŽ­¹s~Óï¿ï´~œù;ªOã  þ"`s¢ÌŒ 8‘§ôcÇ”•™¡‹/»ÂÕ%Õ‰’’mÙ¼YRéþ£5R×nÝÔµ[7}ûÍ7š:å{Wè:ƒ.¿B^^^úaêÔz®Ù«I“&zýÍ·´kçN=þØ£®.Ç)ÎÅ1*..Ö÷ß}g}ÕUWÉÓËË…9×¹8FþYØœh×ÎíÖ×F77VR· 4~ÜXëû èλîRÿ5솴jå íÝ»×…º†———]~¹ 4sæ W—cõÅçŸÉÍxþ|?ÏÄž1:räˆ>›^S§M·9öÌÓOiëßWÚF“&Mª5FgÚ·­cÇŽzúÙ1Z½j•^š4±Ò~¢¢£uë­·©M›6’Á ääd}÷Í×JNN®ôúòª3FíÛwИqãlŽm¯®O?ÿB¾¾¾~ã ºlÐ õ0P!!!ÊÌÌÐÜ9s4í‡d±X*½×ËËK—^6HIÝ»+:*JF£QGŽÑšÕk4û×YµžeUÓçííã£ë¯¦î=zÈßß_GŽÑÌ3TPÆþÂÃÃuÍkÕ¾CéĉÚ²e³¦|ÿ}µþcÏï($$TW^u•ªÂÂB0²ÞìÏÔ¯ÿhÞïs•žî¸¥çºtíªÇBF£QÉÉÉ:v,M­ZµÖ¸çžWƒ *½Çl6Ûì×5ôºëd4«ìcOÊëõþþþºäÒKuüøqÍ3Çæº´£i•Þ_Wcäï _œ ‚ÂB­[¿N¡¡aêСƒÚ¶m«_x^7l8ãýգÇYŸEpp°úPíúîºûõèÑC;vîTNv¶š5o®o®ââbýôã® ѳcÇ)::ZyyyÚºu«òóó©k† ‘›»›¾øì³j÷ï(?þ9ÅÅÇ+##C®Y£€Àýß½÷ž1Èlß¾ƒòIyyyéàÁƒúëÏ?¢nÝ’Ô©Óšðâ #Ij×¾½ü yûø(--Mk×þ%ƒ Š‹ÓwÝ­#GŽèÏ5k¬××f\kâÆ›†+¾Y3mÙ¼Y)))j› n¼I±±qzåå—¬×-X0_Ão¾YIÝ{è“?VÞi¿•°ðpµm› ÌÌL­ZµÒ©5°EÀv[²x±†^w½¢££«¼fÊ÷ßUyÎ M&ÅÆÆÉl6;ôÁIÝ»ëÛo¾ÑÔ)ßK*Yó℉jÒ¤‰zôè©y¿ÏuX_5åææ¦«¯¹Ff³YÓ§O?û Õäéé©÷Ý/£Ñ¨ÿ¾ûŽæÏ›WzÜËKcÇWddd¥÷Y,›ýº®2äÌÛžíÙ“"©t&Ú%—^ªãéé6mœI]Qdd¤Öþõ—^~i’ŠŠŠ$š4â¾û5ò¾2›Í•Þ[Ý1:|ø°õs·hÑ® ¦u›6zhô(9rD’Ô¯ÝwÿH]~Å•l=üˆ¢££µbùr½ûî;6Ad\\œšÆÄT»oGºêꫯíÉÉ?n¬òOÎëÝ»F=ôP¥÷è‘G•———>üà}Íùí7ë¹.]ºêñ'ŸÔŽÒ}÷þŸÍ ®òª;Fzô±Çåíã£ÿ}õ¥~œ>Ý:CÐ`0¨{Ê͘¤h¯ IDATɱ¹§6ãZñÍšiü¸±Ú²y³µæ '©[R’’ºw׊åË%I¹99Z¶t‰ú^x‘z÷é£ßf϶i§_¿~2 Z0žJJJœZ3[UÿUÿx‹åŒÚîÝ»´zÕªZõÑ AÅÇ7Óã?!OOO}ôáÚ¿o_­Ú,ïPjª~˜:ÅúþD^ž~Ÿ[:³ªY³fë§6z÷é£ÐÐP-[ºT‡RSÖnç.]äçç§ämÛ¬áš$äçë«/¿pX?µU—côéäO¬áš$ý2s†<¨°°0%$´«ò>gQyS¾ûήIÒ‚ùó•­  `Ú\›ÐN-[µRvv¶þ{Z¸&•Î,]0¾Sê<›‹úõ—$}ùåÖpM’/þC›OF§»ô²Ajh2iá‚6áš$­^½JË—-SPP:]pA•ýVwŒ.¹ô25lØP7lÐôiÓl–ß´X,Z¶t©¶nÝZ­Ïê,Kÿa פÒ}ߦL)ýôïoîÍ99SôôÙÆƒA^ÔO‹E¿ÏuýÿHœoØÎsK—,Ñ*=W~A{øøøhê´éš:mº¾þö;½ôÊ+ Ô£<\aYÁÚÚ–¼­ÂþUG•$ùúú:´¯š0 ºfȵ²X,š6퇶ݲe+IÒúõë+œÛú÷ßUΪku5FÇŽÓÁƒmŽY,mÚ¸Q’Ô¬yóJïsæ•·u«íþt‹EÇÒJ—Õ<ý9´ïÐA’´bù² ˺’¯¯¯ÂÃÃURR¢m•„T7Tü.JRbb¢$U¹GXò¶m’¤¸¸øJÏÛ3FN>»ßëÁìÕªl<ù,oÓÆÒå1OÿžnÛºU{÷ìQ\\œbbc­Ç;tHTHHˆ6mܨC‡9·`°Däy®lÛ臶9^›Ùkf³Yÿ]&4lØPMš4QTt´žü÷SzòñÇ”sÚòlµ‘q<£Â±â“3˜ÜÜÜÖOM%%uWtt´Ö¬^­½{ö8´í€€Iªt/*³Ù¬ŒŒ …„„8´Ïš¨«1:^ÅÞ~Ç—÷÷÷¯ô¼3Ǩ¼ŒŒŠÏ¡l¶ñ´çP6nõ-8){†™™™•.IXÕþŠ!¡¡’¤?õôÛ÷õ5UzÜž1 >ùì׳gW^eßÕãÇK* 1ÝÜÜlžïœ9¿é®»ïÑ€õñGJ’u˹sû?-¨6hé’%zÝõjÔ¨‘õXMg¯IR~~¾Æ>ûŒõ}tt´ž;Nºõ¶Ûôî;ïT»-ƒáÌ“,-–Ê÷Ôª/† ½V’4퇩NëãôÙaeÜÝëÇÏÛQctöïBåÏÁz^•Ÿ¯‹1’Tåþogr–äTg~Þ5+læÌÊË­zFÞÎ;*=^“1rå³;›Êj;Ó÷wÑ¢Eºå–©wŸ>úü³Oåéé©Î]º(++K«V:nOKÕW?þ—²X,š:å{ë,6Gì½VÞôî;okì¸ñºð¢~úéǵÿ~ëùséL ww ÷8¬ŽºÖ±S'ÅÆÆiãÆJNNvxûe3¢‚‚‚+œ3UÎØªÏÊfíTží»\ñ9HR``$)³’dΣšJ;¹tddd„Sû±÷y—}çüüü+̲’¤   JûIKKS@@€–üñ‡vì¨^µwï^­Y³Ú©}­\±Bϯ뮿^-Z¶T\\œ6lÜ O?ùD“^~Y’tâÄ ›{]~…""*îíuý°aÖ×G­4`“¤÷Þ}W™™êÜ¥³úõïo èÖ¬YSë€M’Þ|ãuÝ~ûêÒ¥‹Z¶l©=))zyÒD™Íæ3l™š4i¢n½õ6uîÒE‹Eë×­Ó·ß~£í§íÝU“1òñö¶yFeBCCmŽ/\°À!AÌÑ£Gõø£è²Aƒ””Ô] íd4uôÈÍøù'ý:kV•÷¦¤¤hâ‹/ê†oTBB;묾?-² Ø$ûŸwQQ‘ÆŽ£aÆ©{žê–”¤Ã‡ëÃÞWAAA¥›$­_·N¥ÁW^¥ÄŽÕ!1QÅÅÅ:––¦… ækÕªU:zäˆõúÚüŽÊ÷Õ!1Q»tQaa¡RSSõÙ§ŸjËæÍÖkëz\%éÿûJ-Z´Pÿ¤´´4ý2s†~œ>½B\^Ù,¾Üœ­\±Â!µ¨C³Öªþk»=öÄêÖ-Io½ñ†/þÃ%5xzzꫯ¿Qzzºþïî»\RC}VÆgöO£gÆŒQbbGó¬6oÚtöN3äÚ¡ºiøpÍúåMþäc'T º˜Á8»»»RvïÖ®;µté§÷ ââbåääØ¿vèu¥Kî-«z¹¹óU]ìÇUäçç§ÁƒËb±è·Ù¿ººà¼GÀ8Pqq±¦|ÿ}õ×¾CÝ?òmONVjêA—(>>^qññ:xð ¦L™Rgµœ+êzŒ`?Æè”¡×]§¨¨hµïÐA¾~~š?ož8àê²€ópÛµk—–.Y¢æ-Z(&6VnnnJKKÓO?þ¨iÓ~Pîi3Ûœ[ºví¦¸øxeeeé—™3õÕ—_¸º$b6À.FWœKØ;°v `ì@ÀØ€ °`6Àl€Ø;°v `ì@ÀØÁÝÕÿ­n.¯  IÒΟRöÞ½.®èÜàîí£„;ï´¾_ÿÞe))qaEpÆÀ?à ô—o£Æ’¤Ô•+تÉÝÛKqW^i}¿ñÃTBsÎc\ü“°9‰›››¢¢)"2JAòôô”¹¤DYYYÚ·7E{÷¤Èb±¸ºÌsZÜà+•øÀÊOO×ì[n–¹¨ÈÕ%9…_LŒ|ø‘$©(7W3®¹Ú)ýø6n¬ŸL–$ççëç+׺ÍsmŒ"º%©ÇóÏWznÚÅ묎đXéäï¾Ó¦O>vZ_çÚ@}@Àæ$Íš·TËÖm$IEEEÊÉÎRƒž VPp°""£µzå2™ÍfWzn2¸¹©Åõ×K’¶ÿ0õœ  nnºæ×ÙÖ÷³n¦üôtVäxçú#¨6'±È¢û÷i÷®:žžn­®®Ýº+<"BqñÍ´c{²‹+=75é×_>áá*ÌÎÖî™3]]Ž$iîw¸º„z¥ºc”Ÿž^§³ÃÎ$sç­yi’$©¿¿Úß;ÂÅ9—3Gõi\ÀÑŒ®.àŸjÇödý¹z¥Ò³Y òè‘ÃÚ¹c»$)úä~]°Á`P‹aÃ$I;œ®â'\\Nw®ŽÑ‰´4í7O{çÍÓÁ¥K]]ŽS«cõ3ØœÄ\RRå¹ÌÌ I’§§W]•óÕ«·|›4Qñ‰ÚùãkׯiS%Üu·BÚ·“ÅlÑ‘¿þÔ†÷Þ×ÀÉ“åîU:V¿ßs·²RR¬÷\öõ7ò ©ÐÖÒ§ŸÒáÕ«mŽy˜L °Ndp3êðš?µþÝwl–˜,¿—XÖžý~÷ÝÖsýßÿ@þqq’¤eÏ>«C+WTÚ›§§ÚÝójtá…2º»ëø¶mÚüéd¥ÿýw¥×—w¶12EEéâÏ>¯ôÞŸ®¸\%……Žw{vŒ¢{÷–$mxÿ=åæªÅ°aj©¼#G´íÿÓž¹s*mÓ`0¨qÿþjzñ%ò‹“»··òÓÓ•±s‡öΙ«ƒË—IµØ±¦ÏÛÃdR›ݪèÞ½åá뫌äd­ÿïÏÚŸ§¿¿š_½"“ºË'<\³Y¹êà’ÅÚþÃ4ŸÈ;kÕýyøø(þê«Õ³—L¢e0º)ïÈ¥mÜ ”Y³t<ùÔ,ÜšŒkMt}úi5ê{¡$iãG*+%Emo¿]¾Mšª03SûæÏ×ß_~aíÏ/6V>øP’d1›õëM7Úü^ F£}û<$I«^|Aû-rH­þ™Ø\ÀËË[’”›—ëâJÎM-o¼A’´{æLfg;¤MßFÕ÷7åa2YE÷î#ÿØ8ÝÜÒ‡£$§à6m­ï£{÷–WpíÐ~º=;F]»Z߇&&ª÷+¯jñcž5dsÆ•™Ô]¡‰‰Ö÷¦¨(]ðØcÊO?¦Ãþis­ÑÃCIcÇÙ|Iò —Ox¸¢zôÔÏW]Yç3¸Œîîê5q’[¶´ NHPŸ×^Õ±Í[ª¼Ï/&F½&½$¯  ›ãþqqò‹S£~ý´øÑGϺ§_uƨaD„zNzI¦¨(›ã¾Ë·qc6o¡ù÷ßwÆ~œ-¸m[%Üy— ÆÒ ÙÞ¡¡j1l˜|›4Ñò±c$IY»wëØ–Í nÓV£Q/ê§í?Lµ¶šØÑ®feéà²euÿAœSØê˜Á`P“˜IÒþ½{\[Ì9(¼KW4k®’ÂB›?×Vûûï·†k%Ú7ož¼‚ƒÑ-éŒ÷­÷ëì¶„»ï©z”W’ŸoÝßKF£:?ö¸õܺwÞVqî©ÀõÄÑ£•¶áѰ¡üccµkÆ ù5mªöí%IÁmÚÊ/&¦ÊYoör÷òRD×®:º~ 22Õ£‡ŒrkÐ@íGܧ…>På½Õ£üãǭϽaC%ެº½Ê„&&êèúõÊØ¾]1—^j»˜A—WØî¼Ó&\ËØ¾]i›6•¶Ó¡ƒuvY]‹¹ì2›píø¶­Ê9pP‘ݓ޹s¥÷ÝÝ•4f¬õ{v|ÛVí[°@Fw58P~M›Ê·Qcuzèa-{ö™*û®ÖïÈ`P·gÇœ ×,Z½ZY)»ÕÀä«ðÓK©öãZQ=zêDÚQ¥._®À–­Ø¢…$)²{w…wéªÃ«WI’vÿò‹5˜n2`€Íçn|ÑEÖ×ûæÏ“¹¨Èéu8·°Õ±fÍ[Êß?@™ǵwOŠ«Ë9ç´ºñFIÒžß~;ë êò Ux§NÖ÷«'NÔÁe¥ûouzøaÅ\zY•÷–ß§«åðág ØÌÅÅÚ;ož$Éàæf°\²¤ÚŸçÏW_ÕÅ‹e0téÿþ'ïPI’l¬Ã6IÚýËL­}ë-IR£ /R×§ž’$µj¥†Ê=t¨Òûª3FÅ'NXŸ…WPÝALæ®]ZòÄ㲘ÍÊÞ¿OF?$©ô”ça2)öŠÁÖ÷Û§NÕÆ>´Y²Qß¾2ÛÕ¿#4ºðT¨shÕ*-öY,'$¨ïëoTzOT¯Þ25j$©tVÖ¢‡²Ö¾û—™ºø³Ïåé﯈nÝdŠŠRÎÁƒ•¶S1 ïÜEÍ›[ß/;F©+N-oéÖ "’lèÚŽkMççkუt"í¨ŒîîêûÆ›Öà²Ñ…}­ÛE‹ÔaÄ}ò0™äo ¤îîŠêÙÓÚ^ÊìÙN¯À¹ÏèêÎ'aájÕ¦­ µzå Yj±çÓù(¤];'$ÈRR¢äï¿;û ÕÔº•d0H’ò¶†k’´ã‡iëÇ!,¥._~ò¥EY)§fAº{û8´«òŸ}ÿ¢…6AŒ\|¥÷8kŒNwhåJYÌfI¥ASwÛg’ · $IE99ÚòÙ§öZÛ¿h‘Kf,ùÇŸz†;œnýïÁ±M›t|ÛÖJï »àTœµw¯Â.謈nIŠè–¤à„v:qôˆõ|pÛ„JÛ¨î…uêh}}xõj›pM’J uà?Îð ëFê²¥:‘V:ãÓ\\¬Ý¿Î²ž ˆof}]RX¨=sNíÑ×dÀIRx×®ÖÇ““•¹kW]” àÇ ¶:¬.]“d6›µjÅRå±ÿšÝZÞx“$iß‚Ê;|Øaí–Ÿu–½¿Í¹ìýûd±Xd8À¹Zщ6³­Ì……§NW£ÅbQÎÁå(çàë³jà_é}ΣÓdfZ_—”{§“wh˜õuÖÞ=6׺’ÑÝ]åÂÀœÓ¿wûö+°e« ÷ù”û<úöU£¾}«ìÃ30°ÒãÕ£ò}eìØQåu®–sÀv–^îSß[OÛïéî_fªÙ!’¤Æýúkó'ŸØ,¹ç7f¯¨f°Õ?%õè)ƒÑ¨5+—+ýØ1W—tÎ hÖ¼t_*‹EÛ¾ýƱÊý NŸUh±T<æJuUKeŸÛl±=§ŽQÅ«wYù¼­ ãéNŸÍj±˜kݦѽâÿ?aו{vçÖlÛS…Ÿ^wö¾}JÛ¸Q’䢈¤$E&u—trïÅ ê®Lç4f°9YÆ&uïÑ[îîúkÍ*>\ù¾U8³V7•κ9¸t©²÷îuhÛ…™Ö× £¢lÎ5ŒŠ’Áè„ÚéÅ©ö lΜ>«§2£Q>‘‘Ê-·‡WÃÈHëëÂ̬ ÷8sŒjêDZšõµo“&2zxT{9HK‰mÈåÖ ÁfÀÙ÷¼ÍÅÅ*Ê˳Îb3EEÙÌ&3EGWÚK~¹pþï/¿Ðß_~yÆÏp:{ÆèÄÑSÏ. Y³3\éZe{ÒUö¾0«â÷t÷Ì i×N’ÔqÔh¹yzJ’.]¢¢œ'V àŸ„lNäíí£î½úÈÓËKë×þ©û÷¹º¤z£ÿûhàäÉ8y²ZÝ4üŒ×ú6i¢¨ž=%I[¿ùÚ®~:=ô°µŸîÏ=_é5Ç·m³¾6EE)¬Ó©}®â®lWÕe1›e))±¾÷ðièÐö‹óò¬¯}BC­AN`‹ò ®VqƒO}öȤ$y‡†ZßgîÚismmÆÈ™ŽmÚd]R³¯¯ÚüëVë~{e»t‘Ñã½E9Ù6A¨o“¦UöS“ç¹óÔ3Œ-÷= hÖ\Á­ÛTzÏÑõ묯÷ë_é¾{Fww58°Âq{ÇèÈÚµÖ×]»*¢kW›ó£Q‘IIgmÇ1ƒY¯'O¶îv&Q=zÈ'<\’dôðPì僬çÊ?ã2/VáÉ%FË/›ò+ËC¨>f°9‰§§—º÷ê-mX¿V{÷¤¸º¤zÅ·qck¨QþÜ•iyÃ’Á ÃkÖ(cûv»úñ •o£Æ’dh•—sð Ò·lQP›ÒP£Û3Ïj÷¬_ä¬&TÙvXÇŽ6µ—ÉBåéçg}Ÿº|¹ŠÊ…0’”{ø°L'gÌu=Z{çýnÝS-uÅ åÖ|Ÿ¾¬={¬{Ç=<Ô륗ulóf5ºðÂj·ÑüÚ¡j©‚Œãjܯ¿õøñmÛ”{Èv&¦½cÔ¸_?ë~iî mÃÅF]$ËÉP¬¤°P/®vͧ+ÌÊRʬYŠ»òJIR‹aÚ˜¨c[¶H³‚Û&(°eKý|Õ•f¶çç+{ß>ù6i"IêüÄÚ=s†u–SúÖ­Ê9¹ßWMž÷þ… ­3©¢{÷V¯I“”³ÿ@é=Uìù·áBµþ×­ò “):Zú©ö/Z¤GÈèá!¿¦1 ïÒE jïܹ6÷Ú;F‡W­Tæ®]ò‹“$õxþZµJY{RäѰ¡Â;wQaV–RW¬°ÞSÛqõôó³þ^%ÉàævÖ:Ý<=uá[ÿѡիؼ…µ^IÚ¿ha…ëÍÅÅÚ3gŽš_wõXnjªŽnXÖ¾  ›“´jÓV&“¯JJJÔ¸qS5n\ùì—¥‹Êl®ý~KÿT>ááj|ÑE’¤mNœµî·Õçõ7äîå%“I-®&IÊ;|X^AA§f8•[ٱŠ7*¬cÇJÛkqÝõ6ïçÞyG…€mßüùj}óÍ’¤öíÒ¾½õÜÿ³w×ÑQ\mÀŸÍF6î !J܉àÁÝŠÓ¯Ô…zZ´X‘z¡un¥ÅÝ ñ‹'Äu“ýþÙ²l6ÉF[x~çpÌÜ{çÎÜ%™wÞ{¿øB‹l•……H;u Ö½z aè쌲œlß½+7­Þ£ÄååÈ»q]šñT§F,Fôúu2Ûš3Fþ³ç(œzÓïÝ÷¤¯¸¿E6ˆùþ;ètê$ÍL¬»M‘¸s'|Þ| gk ¯×fJ÷E}ñ¹4ÀÖœëú×~t2D:ý¢™¯Ì|ýP]QÌK—`îç'W§º²çÌGèÒeAdd‡1cäÊIùNiÎI$\X²=V¬¬Í`ѽ;,ºw—–yt Æö×:igÏÀ²{º ,³=ãÂydDDÔ['åÏ}p?^ȼuàÀ¿k­E"""""""""ú×ã‘mDåÁCf¡PC##…eªP-牓  ‘{ý:rbbÚì8ù‰‰8ñö[ÈŒŒ„¸¬ â²RdDDàôûsdÆH\^ÖjÇŒûãw\߸II—•µúþËk×àÖÁƒ¨*.†¸¼™—.áÔìÙ ¬#&ëü¢ÅHýë/T¢º¢9×bpzölä^¿.S®½Æ¨¹ª+*pff r¢£QU\Œ±¥YYȈˆÀ…%K ./¯·nÊŸûpùÓµ(HNnôº){½kÄbœž3)ûÿDE~>ª++‘{ã:NÍž…’»ú$%áðK/âæ¯¿â~|<ªJKQ#£ý+‰ŒŒ0ø×ß ¢¦†³ÎS˜ÒTTU!©®†ä‘ —‰»z­]  6{hïèQÒ5½¨}Ljšçq£ÀyóЩwµŠ [·*UßvÀ@øÍšÈŒŒÀ™yóZ»‹DDDDDDDDDô˜ã‘ô¯¥ij†¸Í›Q]QÑæAý®]ºt2/_FYVªJK ef†N}ûIËdFF2¸öˆö#jŽQ-Cggh™›CÛÜÎS¦H·'lÛÖ½"""""""""¢ÿ*Øè_ë~\,îÇŶÛñÔõõ¥ëT=ª²°×~ø¾Ýúò_ÑÞcDÊãÕr3û…ÉlK;}YQQÔ#"""""""""ú/c€@iv6’÷îQ·nÐ45ƒºŽjÄb”d¤#ëÒ%$lÛ†²œœŽî&µ‚Šü|Ü=q‚As"""""""""j6®ÁFDDDDDDDDDDDD¤•ŽîÑ lDDDDDDDDDDDDDJ`€ˆˆˆˆˆˆˆˆˆˆˆˆH °)6"""""""""""""%0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆ”À‘`#""""""""""""RlDDDDDDDDDDDDDJ`€ˆˆˆˆˆˆˆˆˆˆˆˆH °)6"""""""""""""%0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆ”À‘T;º+MM-ØÚÙÁÐÐÚ::‰D¨©©AQQ!îݽƒÔä$ÔÔÔtt7ÿõÞ›5AÁÁXºd1¢¢¢Úì8Ÿ¬Z®öö²ÛV®@Ä… ë<ýÌ39r”̶?÷íÃÆ ?¶I[ÓÒeËáìâ‚ù~ˆ7®wtwZÌËÛÍ_€k×b°pþ|éö.vvX½f-bcoâùs›Õö˜±c1uÚtl Ç–ðÍ­ÕåF)£9À@,^¸ÑÑWÛ­?M1dèP<÷ü صs~ûõ׎îο†¾¾>&MžC]]“'N@UU•L9@€þbРA°²¶FEEboÞDxøf¤¦¤Èµkai‰/¿ú•ÈÎÎÆÅ‹‘ØŽÊÊÊö:5""""""""¢'lmDßÀNÎÝ ‘HPYQâ¢B¨ª©ÁÐІ†F°¶î„³§O¡ºZÜÑ]%‡„áE#@pH0:w¶i´ÎÕ+WP^V°wp€ŸŸ_›ö±µ¸¹»ÃÙű±7‹àŒ7°cÛöîIëxÇèIöêk3áçï´{÷pðÀß(--«÷‹ç_xƒ†X,ÆÍ7 ¯¯€À@xûø`ñ¢…¸yã†Lùâ¢"lÛºÚ:ÚèÞ=£ÇŒ…ŠŠ ~ùùçö:5""""""""¢'lm¤¸¸‘Î!';K&KAWW݃Cahd G'gÄÞäÃóƒCJÿnÝɺɶ«W®ôŸ °ÿx£œ]\àææ†ÄÄÄ6É芾ªÊ ˆkõ¶yÜÆèI& áíã‰D‚çÍEaaa½å<=½0hð`”””`ÞÜp÷ÎÿdP¾þÆ›xýµWQ]]-­S\\ŒðÍ›§OžÄ²+áëçÏQ;àlm¤¸¨éi÷ä¦+**D|ÜM€¹…EGtž`ðôôBjJ ._¾ÔÑÝiuÁ¨íÛ¶¶IûII‰Ø·o/âããÛ¤ýG=Žcô$ÓÕÕ…P(DAAÂà 6 °{×.ip ví܉´´4˜™™! 0PaýÔÔT€¡¡aëtœˆˆˆˆˆˆˆˆˆÄ ¶P#‘¨]oçI£©¥…!C†"´G(LMÍ TQÁýü|$ÄÇãÐÁƒ N‡ˆ±cÇÁÆÖUUUˆŒˆÀÆ ?¢´´´Þò®nn1b$œœ¡££ƒû÷óp1ò"¶n GAAA[b“ tîÜPRR‚ÜÜÜ6?æ¸ñO¶oßVïþ†Ö¼›6}:F‹Ÿ6lÀ¾}{eö5w\ß|ëmôìÕ kV­Bbb&Mž O/ohkk#''ÇŎ튳¸ºØÙÁ××wîÜÆÅÈÈÏÝÊÚÿûß3puuÄÇÇ#|ÓõÎ&O™* ÜÕil ¶?ÿ]]]L< C†EXÿ011AAA><ˆÛ·CòàÞoHccTG "Àˆ‘#1hÐ`˜˜š"77§NžÀömÛäû&&&èѳ|||`ai }}}”••!9) ‡Äùsçê?†@€={bРÁ°°´„¶¶6 pçömœ={ÇŽUØ?L™: Ý\]¡¦¦†ØØ›Øøãܹs»ÑkЖ4µ´0bÄHÃÂÂ555HMIÁÞ½{páüùV?žP(ÔÔT7XÆÃÓqA¶‰‘5z4|||ŽUݺkªŽGDDDDDDDDDm‹¶v¦­£'g@Nvv÷¦} …B,^²vv]‘——‡«W¯ ¦¦ææ Eee¥Â@LhžèÕ»7RSS'''ôí×úúXöñÇråë¦U“H$HJLD|\:ÛØ`ð!ðó÷ǼÞG^^^[Ÿrƒ444°ö³Ï§NžÄçŸ}Ú¦ÇëÜÙþH»wOáCúæhɸÖ126²çWÂÀÀ÷îÝCYi)Ì-,Ü`€m\ÝÚk¯ôõ °té2TTVâÊÕ+055ƒ——ÜÜܰôã%ˆ‰Ž–)-&Z:_·nݤÁ¦xþ…‚Ĥ$ÁÁÑ“§L…X,Æî]»¬«ÌMœ8 ŽŽ¸~ý:nß¾ 7wwŒjœññâE2×#$´¦MŸŽ¢ÂB¤¤¦ .6úúúpsw‡—·7öìÞUï´‚Ï=ÿ‚Šòr\»v %%%015‹‹ ,­¬Ø,,,±tù äç#!!vv]áéé…ù â×g¢LAP¼­™˜˜`Á¢Å°´´D~~>bbb ‰àää„Y³ç`Ó¿cû¶†›mÁÜ‹ŸwïžÜþ[·R¶¶¶íÜ3"""""""""R„¶6¦"¢{p(@C¤]äæd#.öFGw¯]ùúúÁή+’“’0÷ƒ÷!‹¥ûŒŒŒaee¥°nž=±dñ"i ¤S§NXµf-|}ý`ii‰ôôtiYooL6%ÅÅX¶l)âbcÔfãL›þ4FgŸ{«W}ÒFgúï4vü8ìܹ£I™TMÕ’q­3fì8¤¦¦`ά¯¤OMMMøû(¬cee… à`dffâÌéÓ ¶oii‰¨Ë—ñÉÊÒì®á#Fâ™3ðÊ«¯a櫯 ¦¦FZ>&&111ú6V©[7WW¼ýÖ›ÈÊÊô ë¯Íİáà °)3FöX²h¡´ŸúúúXºl9¼¼¼Ð·_?=rDZöVj /\ˆ˜˜h™vÍḬ̀â“U9j4N?! äµ/ 4ååxóב““#ݧ!ÁÛË[aß‚‚ƒ±yÓ&lÛº@mÖØÒeËaccƒP9|¨Ásk+ï¼û,--qìèQ|ÿÝ·Ò¬/+kk,\´“&OÁÅÈ‹2ס¥ÔÔÔb±â 6ccc@~þ}H$XZZâù_Â;·ñóƸÿàž061iðX555PUSƒ@ hÕ{œˆˆˆˆˆˆˆˆˆäq ¶6¦"ÀÔÔ &¦¦ÐÕÕƒ@ Àí[©8wö´Ü4n;ƒk%''Ëa //×®Å(¬{òÄ ™,£»wïJ×§²wp);þ©Ú)ö~úi£4¸ÔNµöÇï¿!77݃‚ ££Ó²ú177Ghhäädã䉭ÚvKÆµŽªª*Ö®^-“UXVV†S§N*¬3flm0j×Î2Á1E6nøQæžûsß^éÚVîîÖoª­ááÒà;zEEE022np},eÇèìÙ3Òà |sí–ýÂÂdÊ^½zÑÑWå‚.YYY8yâ8ÀÕÍUfŸ¾¾>TTTwÿ¾Lp *ÊËqá‚âé3ÒÓeÖÄ++-ÅáCÔ®1×<<=áäìŒôôt|»~4¸i÷îaû¶­r×®¥êÖÚlhý5‘H¨¨¨íÓ !Càåå…áÃGÀÆÆ2å)((€P(„±qÃ8"""""""""j9f°µ1±XŒ=;·A @SK ÖÖàäâ CC#œ?wºÃ¦Jë©©)€ÐÐP\¿~ —.^T¸~Ú£bcoÊmË~0Ŧ®®®t›H$‚“³3jjjê]O©ººÉII066†]×®rS¶§òòrŒ;¦]Ž5fì8¨¨¨`÷®]Ò©[KKƵÎñãÇ”ªcbbŠ^½{#//Çk´|nn.ÒÒÒd¶I$\‹‰••}U©>+òègU"‘ 7'ºººÐÕÕÅýû÷ë­§ì]‹‘\ÆÄÔ~žííêÍbêÒ¥ 졯¯UÕÚ¯ÿNÖÔÓÓ—)›•™‰ÒÒRXZZbÂĉ8røp“× Œ‹“;v}÷k{òzq!€¸¸8€½½}«OMM ÖÖÖ˜;wìÀè1cðæ[oC"‘àÞ½{HˆÇéÓ§põÊ…uóïçËm?ÈFª¥ÛŒŒ¡¢R›˜ùËo¿7ØŸ'%ƒÍÈÈ}úöEAAŽ>Üêí·d\ë¤?üjÌè1c  ±wÏî&e‚ÞW°ÞÞýûµÛõõõëÝßùùòŸÕº>ª…rû€æQ}kæççC"‘@MM ZZZ())˜™›ãí·ß£““ÂöênuÄb1¾ùú+¼öÚLL˜8 &NBnn.’“’Ç+ Þ4x¿*¸Íѽ{úõï/·ý‹Ï>•ž{SSSÀÈ‘£0rä(…m¶Æ÷BXÿxåÕWÔ^ÇMüŽ;v(,_^^ÐÐPÄDGã™§§K÷k¨×n/ɦÈ?ü€ÒÒ2 1Ý»–.YŒ¨¨¨æŸ Õ‹¶pïîxùøÁÔÔ "*<\}üþÛ¯8rø|}ýÐÍÕnîîèÛ¯úö뇭[¶ |ó¦zëI$Oø°’’ü¹o_ƒeîݽ«T›ÿU#G‚ªª*öîÙ#3-ž²Å3Ê6w\ë(ž>ïQëßEEE8xð`“ê4¶•­·^US¦«|T³Æ¨±sz°_EEsç}ˆN:áÔ©“Ø»gÒÓÓ¥Ù³£ÇŒÁ´éOC oãü¹s¸~í||}áææW7w" 0½z÷Æ¢ ê½¶ÊÞ¯Íena???¹íuëžÕ'⤦¦*Ü_TÔôÏ¢")ÉIصsllmáëë‡Aƒãü¹s¸wï^½åë2 ëÍ<¬›†5÷‘©:ÕÕÞýÂÂPSSƒóçÏ!3#é->"""""""""’Ç[H$Ò¨š"Í'*ÀØ¿ÿOìßÿ§tÍ£—^~ãÆÇ¾}{QR\Üì¶órs!‘H ¢¢‚­[ ¬Ô«9UZ/>Óªtõô0 ÿ”””àÀ¿-_]S›‘¤ª* 004h°n[ŽëÆ 555lß¶­É÷Ž‘±q½Û õdµeǨN}çd``@€ÊÊJ”••¨]ó¬S§NHMMÅŸ}&wO˜›[4xœ¢¢"œ‰‰ Ò@š¾~m`43#C.¸¦¦¦__¥ú‹sgÏÌÍÍ•ªÛ‘¢¯ÖNUÚ½{P»72¢6ikk«°Luuµt=Ⱥ©ë¢._nðX6¶¶H$¸tébKºLDDDDDDDDDMÀ[ñðô†•u'¹õ†ŒŒŒáëWû°477å²LžÞÞ>ððð€à‘¹èºví +++ˆÅbdeeµø8;¶o¼:ó5¸ººÉí·±±ÁS&(¬çA–HPP°\_«ãéåM-­&ÕÑÐÐÀê5k±zÍZL6­Iu”¡©¥…ÁC† ¢¼þÙðt™u’““aúCSSSº}ÌØ±°²²ª·N{+ :ššš8ð÷_JgÄ=3ãY™uÆkkkdddàúõk­Ò?e5gŒê„„„Ê|¾utt0qâ$ÀÑ#G¤ÛÓÓk×·sww— œ …B<3ãY˜(~ZYY¡WïÞP°þWmxûøÒLyøoÄ„89;ã…_‚H$’Ù¯!¡wï>ð¨'pÙ%%µŸS-míËíßÿ'`ôè1°²¶–n1r¬­­‘ÈȈÛÐÒÒBeEÄbq {MDDDDDDDDDá‘mÄÐØvöH$(..BµX ‘¦–ô¡nyY®\~²² 0iò 11ÅE…042‚››;„B!þøý7”*‘Á£È¥K—ðÇï¿cò”)XüñǸu+iiiªÑ¥K˜™›#//[·l©·þ‰ãÇ1nüSðñõÅÊU«qçö-H$@dd.œ¯Úµ´´4œ;wÁÁ!øô³ÏpóæMT‹«‘––& øµ·!C†@[[ûöîAQaÓÖ•:wö,&O™ [Û.øòëošš33sàì™3 •«Ó^ãª!aè°á¨ªªÂ¾½{•ª›‘ž'gg|ñåWˆ‡‰© \\ºA,cÝ7_ËdvÙÚvA÷ ²œºuë 6;l&I·GD\@jJJ‹Î©9cT')) -BLL4ÊJËàîá]]]DEEáÄñãÒrwïÞýç³ùùˆ¾z•èÖ­tuõpüØ1ôéÛW®}=}}¼ñæ[x饗‘˜€ÜÜ\hijÁÍÝZZZˆŠŠÂ•+WZtþíI"‘`ÍêU˜¿p ŒÐP¤¦¤   ÆÆÆ°wp€ºº:Ö}ó ӪǕ†ƒõW¯\ÁáC‡ÐÀ¬Yû)®_¿==}tíÚb±_}ùgDDDDDDDDDÿ" °µ‘›×c`aa#ccˆDšPÓÖAMM òó‘‘‘†äÄDTUUvt7ÛÕ¹³g¡®®777ØÛÛC[[ù¸rå öíÝ#"­5ìØ¾ 7®_ÃÐaÃáÒ­;£´¤ÙÙÙ8wî.œ?§°nee%/ZˆgfÌ€›«ºví ÈÍÍU`€o¾þÅEÅ BÏž½±±7;$À¦®®ŽaÃG@,cÏî=M®WZZŠÅ‹bÆŒgáìâ''g\¿~ +—/Cß~ýê­Ó^ã:hà èêêâï¿þB¾’k¦åäcÅŠåøßÿž@$ ®^¹‚Í›7!!>^¦¬m[L˜8Q® 777¸¹ý“1–Õ¢[sǨNøæM°³³Ã€ƒ`llŒ¼Ü\üý×_ر}›ÜTŸ­]‹„á èÓ·/¼}|PQ^Ž7o |Ó&xzzõØîܹƒ_ùîžèܹœ]PRR‚;·oãÈ‘Ã8qüxóÖ8ì@ÙÙÙ˜ýÞ»2t(‚‚‚áàèUUUäååáê•+¸x1çÏ)þnhkß®_‡””d 8nn¨ÀÅÈHl ß,]Óˆˆˆˆˆˆˆˆˆþݼþ[OH‰¨QC‡ óÏ=Çaýºo:º;-¦¦¦†oÖ ===Ì|õdggwt—Zìq#ªŸ¾¾>~Üø ðÜŒgÚì8ªªªØ¼e+ŠŠŠ0ãO·Ùqˆˆˆˆˆˆˆˆˆ¨3؈C•ØŽcG4^ø?ÀØÄ‡DVVæc\¿1¢ú¡ªª zzz000P:û²©ìdÚÞÏËk“ö‰ˆˆˆˆˆˆˆˆH3؈ˆˆÚл³f!88éé鈺|eeeؾÕÕÕ-jWGG#FŽ‚ŽŽ»w‡¡¡!¶m݂͛6µRωˆˆˆˆˆˆˆˆHf°µ¡ïÖ¯GaA!¼¼¼0`à@¨©©aÛÖ--°éêbÜøñ¨¨¨@VV&Ž9‚íÛÚÍG""""""""¢'3؈ˆˆˆˆˆˆˆˆˆˆˆˆ” ÒÑ """"""""""""ú/a€ˆˆˆˆˆˆˆˆˆˆˆˆH °)6"""""""""""""%0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆ”À‘`#""""""""""""RlDDDDDDDDDDDDDJ`€ˆˆˆˆˆˆˆˆˆˆˆˆH °)6"""""""""""""%0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆ”À‘T;ºO--môí?B¡Y™™8öTGw‰ˆˆˆˆˆˆˆˆˆˆˆˆ”Ä ¶väåã ¡PØÑÝ """""""""""¢`€­t²±…©™9ÒîÝíè®Q 0ÀÖÔÕ5àîቻwn#7'§£»CDDDDDDDDDDDD-À5ØÚ»§T*¸~-VV:º;J  Oß~ ƒ­-ÔÔÔ‘‘ŽÇcßÞ½‹Å2åß›5AÁÁXºd1TÕÔ0vì8ØØÚ¢ªª ‘ظáG”––JË{xzbÁÂEˆ½yΛ[oÞ|ûmôìÙ ß|ýŽ9"·_SS¦¦¦€Ü¼<”·â """"""""""¢ÿ:f°µ13s têlƒØ›7PQ^ÞÑÝéPB¡sÞÿ¯Íœ ;;;¤$'#&&ú˜6ýi¼?w.TTêÿH†öè‰Y³ç@¨ªŠøøx¨©©¡o¿~xëwdÊ]‹‰Áýû÷áìâ¹vÔÕÕˆªª*œ?w®Þc¹º¹aígŸcígŸ#((¸å'NDDDDDDDDDDDf°µ!¡PžÞ>(,(@JrbGw§ÃMš<þHJJĪ•+‘ó`ºL---Ìšó>¼½}0hð`üµ¿\Ý={bÉâEˆ‰ŽtêÔ «Ö¬…¯¯,--‘žžH$8sæ4†Ðس{—L;~þþ‰D¸pþ¼LæQS1ƒ­ usuƒ––6¢¯FA"‘ttw:”¦–†‰D‚O׬‘× ´´?~ÿ ¬ÿ€zëŸnß¾¨Ë—pîìÙV=æÉ“'ááé 8uêÉ™’“ˆˆˆˆˆˆˆˆˆˆˆÚŽÀ¡›—¤£;ADDDDDDDDDDDDô_Á5؈ˆˆˆˆˆˆˆˆˆˆˆˆ”À‘`#""""""""""""RlDDDDDDDDDDDDDJ`€ˆˆˆˆˆˆˆˆˆˆˆˆH °)6"""""""""""""%0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆ”À‘`#""""""""""""RlDDDDDDDDDDDDDJ`€ˆˆˆˆˆˆˆˆˆˆˆˆH °)6"""""""""""""%0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆ”À‘`#""""""""""""R‚jGwàq¥ªªŠ¡#F7XfÏÎmíÔ›'Çó/¼ˆÁC†~ûõìÚ¹Sf¯Þ½ñÆ›o"#"°rÅr¹6ÂúÀ+¯¾ ر};þøý·)Ð/¬?zõî [[[ˆD"äåæâêÕ+Ø»w/ÒîÝ“–566Æ·ßÿÐ`{W®DáãÅ‹ëÝghhˆ‘£FÃ×Ï&&&¨ªªBVf&"""pøÐAäçç7ØvC<=½0áBÀ™Ó§ñéÚ52ûmll°ö³Ï‘ŸŸçŸ!ÝþÊ«¯!¬ÿÛžûþÄÇǼ¼½ñÑü(--ÅÌW_Aaa¡\ùÉS¦bÜøñˆ½‰æÍƒD"iöyÕ±·wÀ°áÃáêæ}}}!-íN<…“'Ž£ªªJ®Ž……¾úf >.s?x¿Ñã4uŒž~æŒ9 ÇÃW_~!×Îø§žÂ¤ÉSpþÜ9¬^õ‰tûÛヒÐÐ ö!!>¼?§Ñ¾*"‰ðÛ›¤ÿ‹Å(**­[©¸‰#‡×{½ÖÔûÈÚÚŸùU£}Zºd1¢¢¢‚wgÍD]¾Œ¥/‘)gllŒõß}@€ÒÒR<=mªÌ~}}}L˜8 ~þ~000DQQb¢£±%|3222dÊÖ}àÛõëqèà™ýC†ÅsÏ¿€3gNãÓ5µ÷‹Ÿ¿?>˜;OZ¦¢¼……HHˆÇé“§Qïù){À–mÛ¡¢òÏ{2‰%%%¸uëN<£GŽ ¦¦¦Á6;‚ŽŽÖ÷=jjjðÒ‹/ ¬´TºoãÏ¿@WW“'Nhô3ö_7eê4Œ7á›7aë–-ÝÇÞõz/X¸žžÒïÀ–hèÞ£†ÕýìríZ ΟßÑÝi''',[±ׯ_Ç‚>ìèî´Šçχ··Ìÿׯ]ëèîü«½7k6‚‚ƒ[廤-¨««cä¨Q ……%TTT““ƒK—.bǶmõþ¾ð(333|úùÐÐÐhð÷)e4öóz;;,^ò1455±~Ý78røp³UßÏøUUUÈÍÍÅõk1س{7î=ô{%ý;0ÀÖÆ$ îçåvt7žH!¡=äl!!¡Ö øçï Ø4551wÞ‡èæê ‰D‚[©©())¥•% „^½zcê”ÉÒòUUUˆ½)ý·­mhjj"#=ùµ—Û·o×{,WW7|0w.4µ´PZZŠ”äd¨¨¨À¶KL²·GII1þÚ¿¿ÑókŠîAAÐ××GAAA£eÓÓӥ礭­Îm ‹‘˜˜ -SZV&ýûÕ+WpîìY‡„`ò”©øvý:™öÌÍÍ1rÔ(ˆÅb¬_·®U‚k#FŽÄÓÿ{¹¹¹ˆ‹ƒ¶¶6\]Ýàîî„ø¸z¯{@` ôïŽNN^“ö£œœääd×»ïÎí;­r ˆ½ I†††ðöö··FŒ…¥/‘ ?J™ûèác)R\R"·ÍÓË :::(..–n …@ ¨· ccc,_ù ŒŒŒPTXˆ¸¸8XZZ WïÞ ÄGóæ"55µÞºƒ– °5¤ªª ±7oBC$‚™™BC{ 4´¢¢¢°võ*”=tOÊßGKˆGyy9TUUaai 777¸¹¹!(8Ë—.Euuu“û݆‘H„íÛ¶=±øµ´´0xÈ”••a+}'b¼ÞµÚêÞ«{Ù`ÏžÝøå§ŸZ­Ý“±ãj_¶Ø±m{÷„èñ£®®Ž%K—ÂÞÞe¥¥¸yóªÅÕpptÄðá#‚¹ï¿œœœÛyéåW ¡¡ÑN½®}Ilþ‚…ÐÒÒÂO6´(¸ö°¬ÌLdffôôõ`ee°þЫw¬\¾W®üû¤DDDDO2ØÚXUUNŸ<ÞÑÝxâ$&$ÀÁÑ––ÈHOPûÐÚÛÇ ññptrª·žºº:<<=QUU…¢¢"tîlsssé/9zãÍ·ÐÍÕII‰øtÍ™ ooüoÆ32å ñáܹÒ/_±ŽNNعs'Ž>¤ð|LLLðÁ¼yÐÔÔÄ®;¾y“4»CMM ýÂú£¬¬u–I$¨ªª¢_XvîØÑhù];w`×ÎÚr>>>˜÷Ñ|Èœç£6nøÞ>>è?`ø©))Ò}3ž}jjjضu îÞiy (°{wüªªÂW_~³gÎHƒvúúú3nœÂ,ÿ€Ú[ff&ÌÍÍáçï£GŽÔ[¶=ÇŽ9‚-á›[­=E¾øì3dee¨}3xÆsÏ! ~øÞyû-”——ËÕQö>ªÓÐgæQu÷x÷  ™¡¡=Þã/¼øŒŒŒpéÒ%¬þd%ªªª ð‹/aà A˜ùú˜õÞ»rA]‰D‚.]ºÀÉÉI&ƒ¬!ùùùX´pôßÎ..xõµ™ðññÁ«3gbͪU2å›sÕùöÛõ2÷Pÿðò+¯ÂÛÛý Ä¿ÿjRŸÛƒH$ÂÐaÃPQQ}ûövtw:Ì¡C¡¥¥…Ý»v¡ä¡1µ ^oÞ{-áìâ777$&&":újGw§Ù²²²ðÓ† ÈÍ}|^ü;xà®\Ž’þ¬OÿM †½½233ñþœÙ(z­¦!aþüpvqÁ„‰“ðÍ׊g<èÝ»¼¼½¥/ñµ5333,X´zzzؼiS«~¯?~\æg|###¼óî{péÖ ¯Î| ¯¾ü2Äbq«ˆˆˆˆZ†k°Ñcéüù󨮮–ÉX ìÞªªª8wö¬Âz^^ÞÐÐÐÀë×qùÒ%€ÿC™82e½½ˆ²ÒR,_ºTnz¹+W¢0ïƒZálj§¶ÒÔÔÄùsçðÛ¯¿ÈLVUU…ÿ…“'N´Ê±îÞ½ƒ¼¼< 8Ha&PKååå!|ófÌxö9év__ø íÞ=lßÖò)Tnÿ—Ÿ™ӧe‚'øiÃܽ{W®®¶Ž\\\P^^Žm[k§ó÷¯ÿ³´ïu”¬¬,¬Z¹·n¥ÂÌÜC‡ «·œ2÷Qs¥§§#5%!M¥cfnGÇzïq333ø@"‘à»õë¤ã#‘H°qÃ().F;;¸º¹ÉÕ‰‰P›ÅÖ\q±±X²h*++'gçf·Õ˜Ã‡áÂ…ó€îAÝÛì8Í1hð`èèèàСƒÒhO >UUUØ»gwGwç±Çë]‹÷^óÕM¼}ÛÖîIËäççcß¾½8wNñÏÁÿ5.`ß¾½UÐðIäêê 8vô¨Ì÷SEy9¨=ÀÁÁAa}]==<3cNž87®·mgQ;üü…‹`ddŒÝ»vIOh+yyyøâ‹ÏFFÆpvqiÓã‘r˜ÁF¥¢¢"\‹‰AHh(vl¯ Ô„„öÀ­[©¸—¦xZ»º @ÔåËÈÎÎFÿàˆ?÷í“+V»^Ò±cÇ®}VÚ S0ijiIßÄܳ{W‹ÛkLuu Ž9ŒñOM€·¢._n“ãìÿsúôí 777‡„ 2"3ž}‰ë×­k•õ—<½¼`jjвÒR:xP©º~~~ …¸|é.FFB"‘ÀËË jjjr}kï1êH5558ð÷ßxñ¥—Ñ»O_ìØ.?]–2÷QKœ={'M†®žŠ Šššœ¿pO?óŒLY/o@JJŠÜƒ¸ªª*DGG#8$¾¾~rë¸Ü¹}jªj í6ÈLI©ŒœœlDFF 4´úô鋸¸¸fµÓ ñ èÞ=††† ËhjjÂÔÔ›—׿™=jjj1²vú×=»t¨¨¨`ô˜1ë?&&&((ÈÇ¡ƒ±cûöz§µ´´Äø§ž‚‡§'ôõ P\\ŒØ›7±}ÛV$''Ë”ýâ˯``hˆß~ý“&OŠŠ vlߎȈ xóíw`kk‹”äd¬]³FnV@€>}û!,, 6¶¶PSSCFF:N?Ž}{÷6éòBOOþþ«Áu3ß|ëmôìÕ kV­Bbb&Mž O/ohkk#''Ç•ÞuSôíÚ¹¿ýú«L;u‘®;Z·®fB|<>þx ¦N†€À@èèè =- ›6ýÈÙ5›S§Ž¦–FŒ‰ à`XXX ¦¦©))Ø»w.œ?_o KK|õõ7¸sç6Þ~óMô톡ÆÂÊÊ•HNNÆ?þÐàtµŠ®÷o¾…^½{cÙÇãòåK ëÿ¬]´pþ|\» ö³0`à@ô ëN:AEEééi8uòþÜ·WáÿaÊŒkC:w¶Á‚E‹ ££ƒO׬‘Õë£Ì½×Tõ­9rä(Œ9JúïššL?N®®2÷ÑK/¿Œá‡ï¿ÃСÃ`lb‚ çÏcÃ?àÅ—^†@òïßÇúuëd²ËÆŒ‹©Ó¦cKx8RSS0þ© èܹ3JJJpéb$þøý÷&MƒÝÅξ¾~¸sç6.FFÊì300À6âÒÅ‹ÈËËC¯Þ½‘ŸŸïÖ¯ƒšº:ž}îyàÂùóøêË/d¦ë  „¿œadl UUUäääàòåKصc‡ÂïwwL˜4ö]í!®®FLt46nø‹–| <ÿì ™º¯¯[§©k°‰D" 2AÁÁ°¶²‚ŠŠ ²²³p1ò"þþk‹ƒZoŒHDDDDMÅ ¶6& áåã‡Ðž½Ò®nÐÕÕíèn=Μ9.]ºÀÊÚºººðôôÄÙ3g–ðó÷DE]FtôUTWWÃÕÕÚÚÚråë2]b¢£Ûæptp„ššÊËË‘Ðx…VpøÐ!ÔÔÔ`Рægì4¦¦¦ß}»‰OÿïŒÿ¬¬¬pôÈáV{ûÔÕµvŒnÆÞTú@Àƒé!£¢.£¨¨I‰‰Ð‰àéé)W¶#ƨ#ÅÅÖ†¬¬¬ ¥¥%³OÙû¨%Μ> ¡Pˆ  `@hP\»ƒÂzšÚØØ€ÂiGï<ØÞÙ¦s½û8555ôíׯE}Ž‹8:9¶¨Æ¨©Õ¾?“—w_aW77¬ýìs¬ýìsé5lKýÂúÃÀÀ'ŽC^#k“>ÿ‹xê© ÈËËCjJ ŒM0yÊTŒ5J®lW{{|²j5z÷é‹ÒÒ2D\¸€ÜÜ\t ²+áëë'WGSScÇÃë×!‰ðôÿþ‡ç/@EERSRàäìŒiÓ§ËÔ …˜óþxmæLØÙÙ!%911Ñ0Ð7À´éOãý¹s¡¢ÒðUªªª1jª««åÖUÄÈØËV¬D¯Þ}PRR‚œìl˜››·Ú˜©©©aÑ¢Åè„Û·n!;;6¶¶˜5{œL§¬l|²j5&Lœ===ÄÄÄ )) ö˜5{Ž4C¨!“&OÆk3gÂÄØéii€@/ooXZZ*¬ÓÐõ¾y³vÍCESF?ÌÁÑÕÕÕHxhMÄ7Þ| /¾ô2lllpóÆ \½z¦¦f˜6}:æ}ô„Baƒm¶d\íìºbñ’%ÐÖÖÆÊË ®ÊÝ{Mu12òAÀ"\ú"N|\œtÛ–ðplÝ.W¯¹÷Ñ”©Ópëö-”••¡WïÞXñÉ*Øvé‚«W¯ÀÌÜ3ßx½Þ¬{oÌš]û ýâÅHTVV"¬ÿ,]¶:::žç¸ºµ×÷ëŽaïà€¸¸X˜››ã·ÞÆë¯¿””däçç£g¯^è&Sç­·ÞF¿°0iìêÕ+ …>|V¯ýõ|®1áB¸ººáÖíÛˆŽ¾ g,\¼êêêõö-33C:ʬebb‚•«VcÚôé°²²Bll,._¾Œjq5ÆŒ‹a#F4¹­ÆXXXbéòèÔ©âQVV& j>ò3P7Eví9=:{D}Ö¯[‡ŒŒ Œê)éÏ…u^xé%XXZbïžÝ­\óðôħŸ}†¡Ã†A]CQQ—qãúuâ¹ç_€»»»Lù “&aô˜1ÐÕÑEbB"##?=zöÄŠO>A`÷ú3Ñ•=NÐ=1köUU/ýÙæ­wÞ‘+Ûÿï5Å•¨+€>}ûAOOOº]$Ig8ø÷ßõÖõññAÏž½°yó¦_Zi šZZøhþtîlƒS'Oâûユ+ @{{Åw@íšÓºzzHNJjòK…îsNIDDDôï ¶6& aÛÅNúo3ssØ;:áÆµh$%>þâ;Ò… ðÒ˯ $$ùù÷! qöÌXY[×[ÞÑÑÈÊʽoÃÇÆÆÂÍÍ >¾¾8}ê”´¬¶¶6 i dĵëNµýÍÈÈPøp§µåääàò¥Kðõ󃉉I£‹Š7W|\Ž9Œ°þðÔ„ ÈÏÏÇ/?ÿÜxÅ&²~0ÖéiÊ­Í! áíí ¸U»øåË—áàèÿ€@\º$û¦#ƨ#ee×®É&`nn!óvµ2÷QKeff"9) !¡¡¸ ;»®X¿î›zËò êøRð`»‰±I½ûÏ;‹Ï=‡aßÞ½Íçì¬ÚŒ( s‹fÕo*wÚ@ðùÉT`B¡£ÇŒAMM v6!°ÔÍÕo¿õ¦tý¿~aaxõµ™6|8vï’Í9óuhjiaÏî]øõ—_¤c3tè0<ûüóxíõ×ñÊK/¢²²RZG `ÅòåHMIÁ”©S1vÜx`ÁGB(âÛ‡‡Ìq&Mžÿ€$%%bÕÊ•ÒïE---Ìšó>¼½}0hð`üµ¿ÂóêÓ·/ŒqüØ1dgg+,÷°1cÇ!55sf}…¼¼<µ†¦¬UF;;\ºxóæ~€ŠŠ ¼6óuôéÛC†«wÝAeë¼óî{°´´Ä±£GñýwßJÇÂÊÚ -ƤÉSp1ò"nÝJ­·††F6l8>ÿôSœ>}J:ÆNÎΨ(¯Pxn ]ïXi€­á`·¡¡!LLL””(Í FÏ^½ŸŸç~ }ȯ§§‡%K—ÁÝÝÆ Çž¦¤lî¸:8:â£ù  *bÙÇKÓ`yeï½¦ŠŒŒ@ddm¶â¡Cáãë‹Ø¸ØF×mî}´eófìÛ·^^^øhÁBã¹Ï ´´³fÏA÷  XZYÉe3:99áß—Îf  ñî{³ؽ;&NšŒø^a_­¬¬ŒÌÌLœ9}Za¹ââbÌ}Äb1–.[gi6©­-Ö~úÜÝ=d2éÃÃÃqòÄq™€€@ ÀÔiÓ1zÌLúi¬Z¹RºOCC¯¼úTTTðÍ×_I׃Õ‰°`á"…æÌÌLé˜899Ée*òö;ïÂÚÚçÏÃ×_…²‡2›ºví Û.]šÔNScó¦MÒiö4µ´°tÙrØØØ $$Tnâ‡×Âurv‚…EÃÿŸ–•–bíêUX¶b%Þ|ûm¼÷ÎÛ(**BÏž½Ð»w$ÄÇã÷ß~k•s100À{³fCSK ¿ÿö+víÜ)ý¾ ‘Ë?î,~ûå¹l=?¼ÿÁ\¼ôò+¸tñ¢LdsŽS§GÏžX²x‘ôÁN:aÕšµðõõƒ¥¥%ÒZÏ®5þßkŠãÇŽÂÃýz÷Æ×ëÖ#>>5ÕÕppt„X,Æ7_UïÔ¦"^xéeܺ•Úâ>4F]]sçÎCW{{dddà«/¿¨÷gÁ›7o ´G8:9J3žëS÷rGlìÍ&ßÓÓKp¾s[>³“ˆˆˆˆ:3ØÚˆD"AjJ2Μ<Žû÷bÿÞ]8yìÒÓîA ÀÍà æŠßº¦–+).FôÕ«íŠÐÐHMI‘ù¥ñQþuKM‰X÷÷Gx=œ‰ÓÓ@6¤îX¥¥%mzœG`UUU¬]½Z„€²²2œ:u²Ém4D"‘àǾGEE…ôß{÷ìØ+XóF™:žžprvFzz:¾]¿N&ÐY»ÞæV¹ Ÿ‡éèè`÷îÝ8uê¤ÌÇÇÅ) Ê5v½ïÞ½ƒ’âb8:8JïEMMML˜8 ={ö’–«{Z—ñü3Î;¶o“É ),,Äo¿þR[f€âÏмquéÖ .‚Š@€%K7\”¿÷ÚRKî£ÔÔTÿܯ™™™ÒÿÛë2MMä_ÈÊÊ’ÿêêjüôÓF@Ÿ>}Ì43vvíÜšš…åîÝ»+Í(©ë_ÝÔ´÷¬ñjòHßöìÞ%—m#‘H°ýA ðÑL+ÿ€èéé=x1éŸSEy¹ô3×ZÜÝ=àìâ‚¢¢"|óHp ¨=·ÿ³wßáQTmÀïÍ&¤÷N¤‡ôR@zUB•"ņ+U?ßW±‚E±Š¯JUEéÒ $¤‡–„HB*é}³ß›]²ÙMÙTÅûw]^’™9sÎÌîì&óÌóœÇw[wrsåæ·«®ªÂ±£’`d[ón©âÆøáûïajjŠç/†¥¥%ž~æTVVâã ëå‚W]1~ÂDèêê"9) {÷ì‘û¼‹ÅˆÝˆm?ýMMMøøøÀÏßúúúHMI‘}_·4÷‘G`aa-›7·yv‡Ð°0¸5ÍgeeÕêÜÂÒóîì|/;zà A˜5{ŽÜÜiÒõÍ¿[”Ñ70@øÐ¡xé•Wi©©²ÏC""""ú{`[‰DHJŸ»ª¤ä..FGÐPX÷·‹«òîôíÍŽûݹsg±øÅ—`k;;¶oosÛ `ÉJÒŒ%É¿ã0ÁøùûC(Þû¼Ùñ–O/núâK¹§iýåìØ¾­ G!hê§ »è„øø8äççcô˜1JË7‡¢Èsç0ÁBL˜8III(//W$»wÉ*‘¤ï‘¶^£G#bÊŒ?¾Ó¥a{â:^·~ƒÜÏUUUXûÑšVƒR±11˜9}Z÷W‰¢± IDAT¨@€iÓg@,cOK-Ÿè‹Å(*,„¾¾>ôõõq÷®¤ô¥³³$ûèòåKJßW °³·‡“³3λ—R[[+»'½yÜ<_S+ÉTÒÒÒBEE MMMÜHOWz½Þ¾}UUU4hP«ïñðð¡°²¶Æ…óçeÙqòä‰}£¨¨H.˜ MYª­•´V¥$øâÅh¥e­Ršæ"tttlsœüÙæú–Ú;ßb±×RR ÉÚÈÉɧ—fÍžÒÒRY¦œô&hÊÕ{7«]šÞw‰ ûMJL„X,† ´ut‚Rª¾®ž^Þ˜0a4µ´ðñ†õ² ¼¶tæÚëI]¹Žªª›®Ó¦<šŸ»Zéõª­­°ÏË—.)üŸ—‡üü|XXXÀÆÆFkÎÌÌ Žââbœüp›YlÇŽk·WOi~SzPý:j©å„ó‘ŸŸëiiprvnsŽEi¹Öæ©ÑÔÔlÚ®¦Õ}ܹsI‰‰"—E¥ éMÖšêên+'z#=Õ55Ðì×¶@GG/½ü VÝ} ©MÁ‹¾ Ä\¼ØêSè-)›GE:G‰Z³ŒCCI©ÞÖ‚ÊÒù¦¤%}¥š?é.ýwóe"‘äßÒÖÒÏrGGü²§í2{ººº(kqÃM蓪­­EUe%tõô`hhØj€MÕ×uÊÔ©²sOE"[¿"¦LÅœ¹s[ýN$Ÿ?Ò¾¥ŸaÊæÐkllDII‰B–\gI÷Ó‘ùÍZZ²lú5…¤âbcqøÐÁVÛ”ÜUüVú™dÚtîòT8w>¾¾xañb˜˜´þPUËàigú‘jó|«Ý;ß]ýÞ{xÖ,89ËÏqYTXˆÍ_¥Ð~þüùðóóÃÿûŽ>,[~æÌi4ˆ°dé2<ñä"¼ÿÄb1„B!ž{áTWWãÇn,-ß–¬¬,¬þðÔÔÔ xH<<<ðô3Ï`ýÚµrÛ‰Åb¤¤¦À××OVfß×GòÝâíí 555ØÛÛ#''Gá¼’‡òòòuõõ(.*ÂåË—uáB‡çk#"""¢ÞÃ[¨¬”ÔÄè§©ÙêÍ꺪ª*¼óöÛÐè§Ñfi¤à¦Œ¥††¼ºd©Ü:éµAÁ÷l(++ƒ¬¬$O¾K}ö©$X²ð±ÇÚ¼™ØQÒÀ–¥••ÜÓ̽á¯cÇ0{Î\Œ?[¿û¶[÷­®®ŽgŸ}?ü¹ˆ à!ˆ˜2'Oœ;§%=wÖÖý;ÜÆÎÞ^vS!bê4Lzh²l4310(;wìPè§/^£¾`an@r!??O¶\Õ먻|¾i,,-påòåV·‘Þ”4lh‘24”<^T¤<+PêÈáÃðñõÅè1c:UÎÔÜBòÞº“§úM±Ö|ñÅçÈÌÈ ¹ÉõÚÒeðññÁâÅ/âÕW^îó‡8¦Ïœ@µÀRGK=µWeSÐÉ,ÖÖdfd ::ºÍmš—@” ÂÀAƒ/+×Qe¥Š7ßT!ÍŠi¸eµ:Ó&:*ªÍ²Våå­gYY©J}uô|KKy99;ãÔ©“ðññőÇ1bäHøúù"33ŽŽNÈËË“eMvˆôÙÆûNÕ×5??ï½³ Ï<÷<<<<0áB|ÿÝwm¶é̵×:{µKÉçA{—¾@I####Œ3ååå8ÒlÞ´®hÞËÐaððÑGQZZŠ-›¿FbB"JKKdŸÕÛvì”=ôÑRkŸeÝ•½&ß—êmüýüe%™¥Š‹ÚžÇW,îÙÒ~ÍyxzBßÀàåí]=½Vç*$¿×(,×ÐÐh³ŸŽž; KK¬|ý …BìܱgÏžAQa¡,ˆòoþ~þþ­~×uæ5Rõ|wözurvQ8w-çH”>b$ 6æ¢Âº˜‹!‹amm SSSÂÊÊ ƒÙáîÝ»XüÒKrÛK‡wtpÄ›ÿý/Š ñåÊççmNM­íïÊéé¨nÊ ýê‹Ï±þãO†ðð¡ròpíê5øúúÁÉÙbØ€Ï7}†¿'''ˆDÐÐÐh5óîäÉ“}ö©Ž¶>ÐüÏ>…Öã®\iý¦»T`SY;---¥H’v̓LW¯^Á!!ððô”Í]ÐÒÒÒÐÐÐmmm8::âúõëí7ê&¥¥¥ˆºp¡aa°îßñ UGL6¶àÊ•Ë8uRR~i÷ÎxbÑ"<¹è)¼ûΪ.÷!×ÀÍÍ­ÃÙ¢AÍæT–àiÉÎÎffæ(,””ÔéËר/¸¸ºrrrä‚L¹ŽºÃ­[YJç/i.+K2GÏÛJ×Û6-oo?11Q\\„1cÇaÿï¿©;z¾¯§¥¡¾¾Î..°°´„•µ5ââbahh__?Ä\Œ¶¶6¢££dmD"*+* «§…ò“šššÐiÊf.U’™ÐYß~³999ølã'Xÿñ'xðÁ‡”Øêï=}íuFW¯£Î01QþÙ ÍSöÙðÐähhhà×_~i3«¹³FŒ”6õ•B•±±±Òàšì³NI–“šššì!‘î ½f­­­ÚÙRѼGævÛ8º›……^Xü"ª««qòÄ Lœ4 /¾øVøA«m~ÿm~ÿ­ãŸ[E……055…•µU‹RâÊ…‡‡CCCþü¿ü¬8߬¥•ò×@Õ~:£«×ë‡ï¿×¡íô d¥½k”\o hhh€††ŒŒŒäæ‚366nµ¢€¾|}ýä‚z²ïJ %ß•­<€¥Lnn.vïÚ‰ù bÑÓOãòåKr™ÝÒÀ™³³ tšJŸ>u S¦Nƒ¯ŸŸ,kMY¹N""""úçiûQ-êýmlå,óð7 «« 77ɤÕÏ=ó4fNŸ&÷ß¼¹sP__˦òwR'K‚B£F‚v–o¬ªª’Ý€™<¥ëqª:rø0Æßmû´¶¶ÆŒ™3!‰°åëͲå‡Bvv6||}Öå~P\\ŒVRâGéükŸ}ú©Â{aæôi²?š¥e$¾z“@ Àøñ’÷ÂéS'eË;{õ–¤DÉ\Ivöö 7(ÕÕÕáåí @¾,¨2"‘û fffð÷WDl©©)›¸ÍÏ]w+++ÃÞ½{ÓgÌlµÄ–¶Žììíagoßfy¾®˜1c&€ž ,]O“*=<<•§¯ÜvuíÚUÔÕÕÁÝÝCå9¼½}àìâ‚kW¯âê•+]Gk*š20Ì›²K›“uûŠôÚ2$¤WúSå|×××ãFz:ììì„ò²2¤_¿Ž¸¸X vsƒ—·I6Bsi×%ï'éür-ûÈÎÎîÖ ÒÒˆ………øú«/!°ø¥—Z½)ÜÓ×^sÒßeµ•ÌÙ\W®£Îòððe²J™››ÃÂÒ555¸}û–Ü:]==ŒŸ0ÕÕÕ8xð@ŒI ËU2óå×IJŠä=èíã­°Núžë.ÉI’yÞBá««ÛmûíKêêêxmé2èêêbËæ¯ñÝ·ß ))AAÝRmB*©éÜ;®CÛKß Êªl 4ÖÖÖÝÒOgôÖõZS]- |98(ÎÅicc#Ë”–åÍÎÎVú;úÌéÓðí7[ ñ˜9}^zq±l_ÒïJ33s…kÆu°jß•¿ÿön¤§C__Ï<ûœÜº´´TˆD"8»8ÃÇ×III‰Dˆ‹‹…¯¯ŸlþµkJæ_#"""¢Øzˆ³Ë`˜[X*üò>ÈÎÎ.’_ào\ï™ R¿„B!nߺ%›ä»¹ÚÚZ\¾| |/»éâÅh$'%AßÀ+V®”{‚RMM­Ós4)³í§ŸP[Sƒðð¡˜3w®ÜduuuŒ3Æ=Ðmý5wéR2r²³e“Þw‡§Ÿ}øcÿ~¹Œ!‘H„¾ß xôñÇÊ ©ª±±ßo•ìï±ÇŸP¸Á«¯¯ù ÂÆÆ€äIw{‡¦ùÖâ•î366À½@œT_¾F½ÅÜÜË–¯€½= pàÏ?eë:{õ–ÜÜ\$&&BMM O=ý´,ÓG `á£A__Y7oâÒ¥KíîëØÑ#hllTéšpquÅþû455uáBßT9xàJJJ`nnŽ#G)ÝÆÝÝëÖoÀºõ0$$´ÛÇàîîÁnnHKMErRRû :!55·neÁÈÈHaÎÂqãÇÃÞÞåeeˆQRvJUUU8tð´´´°|ÅJXXȲ¼½}”ò¥eú~ýåç.¡-7ÒÓþ²Ï3ðôôÂÇ÷X¿‘””„ëiipquÅSO?#ËTÒÔÒÂðá#àååÕ-ý©z¾¯]»uuuLŽˆ@||<Äb1.%'£±±ML´,ãõ×±cM}Í”{/èëëcÞüù€ãëò±´&òÜ9œúàÌéS ™í“&M‚¶¶6:ØféÀ®ÈÍ‘SF¶øl4ȳç(Ï‹‰‰AyYvðîý.¡¡¡!{Ïw—ääd¤¥¦BOOÏ=ÿ‚Â5kkk‹¡Ã†ukŸ=má£ÁÉÉ §OÂéS§ ‹ñÙÆ(++ü àââÒþN:àÐÁƒ¨ªª‚——¦N›®p=øûÈ‚+€äw:T®ì¤žžž{îùV¯'UûéŒÞº^ëëë‘Øô Ƽùóå‚ÒÖÑÁ‹RSR”Î{©ŠÒÒRA__ÃGŒ-733ǬY³UÚWcc#¾ø|D"‚‚ƒå¾kkkk‘‘‘GGx{ûÈ2ãccáäì ///”—•µZ2“ˆˆˆˆþYX"²‡˜YXÀÍ讪BCCtõôd7u³23™¡Ú<,Ô3¤eíââ[Ï\‰‹‘†Þ~ÆŒ???¥ëD"¼ùÆÝÒÏK¯¼q£FÆÆ²'©óóóñÁûïÉŽèÚu$Õü=ÓÒ‘#‡qâøñÎ`óW_âƒÕkŒ¯6oAVÖMX[÷‡¹¹9jjj°é³O;4WWQQbcc‚¬Íá­·W¡_¿~°´²’e™$&&bӦ϶Wõ:jOmm-öíكǞxÓgÌÀÉÇ{}.¶3¥4Нuwúü³ÏðÖªw0mútàfÖMXYZÁÙÅ"‘Ÿ¾ µµµ]îgû¶m°±±E@` 6~¶ 7n   :ººprr‚¾¾>Μ>- ¾€‹‹ <=½‘qññʃöÝáÎ;ˆ‡ŸŸ>Z·©©©ÐÑц=N?Ž1cÇöXßí‹ÅX¿n-þûö*ŒŸ0aááÈÌÈ@ii)LMMáèä„~ýúIæÈéâüŒ9ß×®^Å”©Saff.Ë`•>àëë‡ÊÊJ…,§ó‘‘8wö,‡ÅÇŸlDRrD "xyIætºrå2þØ¿¿KÇÒžo¿Ù‚ÁnnðññAÄ”)rå7{ëÚ“ºrù2Š‹‹0pà@|´n®^¹ŠººZ466bû¶mrÛvæ:êŠëiiX°ðQ„„„"//NNN°²¶F~^vlß.·­¦–&=øêëë{ôõÛ·o/‚‚ƒ19"^ÞÞȺy†FFðôôÄùÈHøÈÊŒJÕÖÔ૯¾ÄÒeËñÒ˯`Ô¨Ñ(,,„»‡êêjQXX333¹6Ú::˜<9Bö³©©$paaaY³çÈ–gff :*J®í†õëðß·W!$4^^^¸rõ jkjaii 'ggìßÿ;Ξ9Óݧ¦]AAÁ°wpýle%ù½däÈQððð”-ÿóý²ÖÁC†`ÒƒâÎ;زùkÙ6wïÞÅçŸ}Š×ÿïM¼¶t)–¼öZ—ƒª%%%X¿n-–-_ù `ÜøñHo*>ÈÎýû÷LJ¼/Ûþô©S˜2e*\Ʀ/¾Äµ«W¡®¡//o”ܽ‹K—’á驸ðªýtVo]¯ß}ó >p„í€øtÓ&¤¦¦ ¡¡ÎNÎÐ70@yyy‡æQ눃`þ‚xañ‹?~êêêàèä„3§OꕌÁÖdffâ·}{1}ÆL<ùä"\JN–¯]½'''hji!>Nò}tåÊÔÖÖÂÄÄۙ׎ˆˆˆˆþ9`ë!é×Q_W##côÓÔ„Ž®.êëëŸWˆ›™ÈÍákB¡~~þ€„¸ÖoÆÅÆÆà‰E‹àìì ###Y𥼼¯¯\qãÇcèÐa°0ššš(.*±£G±ÿ÷ßäægÑÐÐPúD©¥¥%,--•U• ëI9˜—_\Œˆ)SáçïWÔ××#//1£qáüùNŸ‡öœ8qÌ›‡~ýú)»•Â1©««Ë-ÓÖ–<ý¬¯¯G›žßúÝ·­Îmò¿­ßa݆19b N?®0Ǫöüú+’““ñàƒÁÝ訨@jJ Ξ=#{‚XZ¾¯­³Y7o¢°°ffæðõóÃùÈHÙºÞ|LLL•ÎÅHžªí.ƒ»A$¡¼¼‰‰‰ˆ½xÇŽ•›Ð¾«×‘T[O[K3»"//Ë–¼†Y³çÀÏßž(//Ǚӧ±k×N¥%šZsäС6lðòöF]]JKKq>2gΜÆÅèh¥A¼ËJU88:ÂÇ×YYY]ÎkÏõë×±bù2Ìœù0¼¼½ŽªÊJDE]ÀÞ_í¶9°úÃ0ì0rä(Ø;8ÀÑÉ ¥¥%ÈÊÊB|\¬Üç̘ù0`Ï/=èøäã xüñ'WWWÜÌÌÄG«?DcccŸØI†Óò¥K0qÒ$„„„ÂÉÙêêê(..FbBbb.vËçcgÎ÷µkWe×dB³Ìeé))×”^³?ùW®\Á¨Ñ£àíí555äææâ·ßöaÿï¿÷x@»ºº?ùï½ÿ™7—’/!=ýz¯^{RõõõXõÖ[˜7ÜÜÜ0qÒ$¥¶Î\G]‡}ûöbæÌ‡1$$UUU8þ×_ؾí'Ù<ØêC6ÝáFz:V,_†¹<ggØØØàÎ\lßööÿþ;¾ÿAùƒEQ.àÝU«ðð¬Ypqu…ƒƒ’’“°õÛo±ú£@îÁmmÌš­˜•cnn.·üä‰ ¶–׬§§ÔÔÔPŸý¿ÿ†ƒz¦|f{ƒ‚”fLI絓:yâ8*++eó®‰D"|²a½Üù$MøóOLzðA¼øâKX³úÃ=hÓ–Ä„¼öÊ˘1>¾¾ B]]rssñýÖ­¸rùÞÜÐÕÕÕX±b9æÌ™_?‚ÒÒRœ9} ;¶oÇ ‹_ì–~:«·®×;wî`É«¯`ÊÔ© ’•/*,Ä™³g°oÏ^u¹ømß^¨««cÌØ±°wp@~~>víÜÇaì8ÕKnþ¼{7†„„ÂÆÆÏ<÷¼lW¯â¡ÉÈÌÌ”]$!)1CBB8ÿÑ}DàäæÓµ¿"ˆˆˆˆšY¶b† ÁÆ?Æ™3§ûz8}bÐ ;¬ÿøcädgãå—^ìòM[jÏ·¯=‰iÓ§cÞüؽkvïÚÙîöøâ«¯a``€ÅÏ?§´Ôñß•¦¦&~Ú¾ÅÅÅxæ©E}="""""¢f°Q·QWWGfFn¤§ãܹ³}=œ>chhˆÝ»váÒ¥äm°§7ñ|óÚë S33=rùùyÛàš‘‘PÑ¢ŒáŒ™C à|ä¹>Ñ¿3؈ˆˆˆˆè¾¡jÛ?ÁÇã…Å/"-5¹¹9hhÁÑÑŽŽÈÉÉÁë+Wty1""""""R 3؈ˆˆˆˆˆþÆnܸsgÏÂÙÅvöö …(,,ÄoûöaÏž_\#"""""êÌ`#ºÏ|¼q£ÊmÞ]µ ÅÅÅ=0š®?a"&Lœ R›è¨hìØ¾­‡FôÏñÂâáäì¤R›íÛ¶ábttˆˆˆþNî·ßˆˆˆˆˆˆˆzlDDDDDDDDDDDDD*Pëëý“0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆTÀ‘ `#""""""""""""RlDDDDDDDDDDDDD*`€ˆˆˆˆˆˆˆˆˆˆˆˆH °©€6"""""""""""""0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆTÀ‘ `#""""""""""""RlDDDDDDDDDDDDD*`€ˆˆˆˆˆˆˆˆˆˆˆˆHê}=€û@ À€v8hô ¡¦¦†Úš åÚUTUUöõï+B¡»~þPTT„çžyrÛlýßÐ××ÇË/.Fvv¶Â>F‹çž°ç×_±}ÛO Û„††aɲe€ø¸8¼ÿÞ»rëMMMñÕæ-¨ªªÂÂùó666ØøÙ¦vãýwßA|||ޏ{EL™Š…>ŠØØX|øþ{=ÒfÕ;ïÂÃÓ?ýøöíÝ«t›ðð¡xuÉâÙ§ŸÂ¢§žÆ„‰@i»†ÇK/¿¸5«?ìÐØ[3`À@øùùÁÅÕ.®®011,zâq”””(mcdd„À  ¸ººÂÅu0ú÷ï@€7¬Ç¹³g»4©Ánnxïýä–‰D"”––âzZ<ˆ¤¤Äné룵ëààè(¿lÍjDGEuËþ‰ˆˆˆˆˆˆˆˆˆþÉ`ëAjjjK++@UU%êë¡¥­ƒƒì››Ã[255E`PÊ   {ÿR`kÎÛÇzzz¨¨¨- ‡@ h³ÝµkW[]WQyÿ¾/ââbááé oŸVlÞ>>’mccÖ……UhÞ­cœ2u*FŒ ‹ÅjãçïgŸ{^ösGÛu†H$•˗ZZZ°±µEð!2;wìÀ/?ïîrGqŒ$°ŠvyŸDDDDDDDDDD÷ ØzÐ`wOXZY¡¬¬q1Ñ(+-•­366A]}]Žîþ'‹1nüx•lý’}ùÈ IDATúõƒ—·7êëëQ^^ŽÂÒÒyyyJ·¿ž–'gg Á_ÇŽÉ–‡‡EZj*œ]\ZíëÍ7ÞèøÁÜGbcc±`á£pss‡††êëë¶ññm °ÅÉؤçÛÊÚwrsºººðõók÷|«"#ã ‘’r ™Øòíwí¶).*Â"åÚ5¤\KÁ’¥K»m<-ÕÖÖbÕÛoÉ~îׯž\´£ÇŒÅì9suYYY]êãè‘#²ÛØÚ0ÀFDDDDDDDDDÔ ç`ë!ý45áà脆†\8wF.¸wYÆu¿¤¤$øøøÂÒÒ²Ãm|||¡©©‰+—/˲§›e´µ”››‹ÌŒ „…•-³°´„“³3ÎGFv~ð÷±Û·n!??pwwWXß¿˜™™£¾¾ÉIIrë.\¸‘H$—±.3v,ƒ‚ñç´Ú&2òfÏ™ }”—•!,,¸u {¬;EŽ••úõ뇑9Jæû'ˆ‹Åø áíã‹ÄDù9äå!/_¾„ÚÚZ¹uåå帔œŒ°ðpìùU2×^XøPܼ™‰ìœæ¹è.õõõÈÈÈ€ŸŸŒMÖ Œ9 £GÆÀAƒ ¡¡;wrqêäIü±?ºm,î˜<9.®®ÐÓÓÃݻň¹ƒŸwïBi‹‡ (8’ùëLLM¡®®ŽÂÂBÄÅÅbßž=­Î{7ØÍ S¦L…½ƒ= PYY‰ü¼<ÄÇÇá·}ûPW§˜%¬­£ƒÉ“# +++466"3#û÷ÿލ ºíÑýl=ÄØDrƒ»° šššprq…Ÿ ¼||Ñ߯¶Ýù¹¨ë.%'#';£G†††F»Û âãã””‘Hwwwèêê¶ÚîÜÙ³ … „ Ç¥KÉ Y‹Ýåµ%K±á“x{ÕªÙofú4Óšóññ•l£8ÿœ;wvvvèoc}}}x{{#òܹžì?ˆ††ä™‰ââb¹åB¡+V¾Ž/†½½=2nÜ@rrŒ 0ÁB¬|ã ¨©uÏ×Á´éÓñλï!0(ùùˆ‹ECƒ&NÄêÖÂÄD1ø÷Ê+¯bÔèÑh‰œ”„ÄÄ…B<ôÐd¬Ûð1¬¬­Ú„†…áÝ÷ÞG@` òòòq>2™™037—¼õõÚ˜™™á£µë0köl 99éééptr²å+0cæÌn9DDDDDDDDDtÿc[Ñi Ȩ««cÄèqÐÔÔ”­³wpBiI .DžEm-³ÛzÒÑ£Gðèc#$$gΜns[ggg!??ÙM™a×®]ƒ‡‡üüýqöÌ¥íòòòp#=aáḔœ{{|õåÝ~,÷“äädÔÕÕa eYMB¡žžžç_“ŠŠŠÂ3Ï>‡°°p””Ü…P(Dä¹sèocÓkãÿ;ÒÕÓƒ½½-?ïàœ¹ 0(éé×±vÍttt°lÅJøúúaü„ 8xà@—Æàëë‡yó ²¢|ð>R®] ^Ï_°S¦NÅO.ºµɵ۵kNŸ:)—©&0oþL6 .ÄÚ5käÚLŸ1ëÖ~$—y&à€ªêj…ñ½¶d)¬­­qâøqlÙüµ,í¿ Þ^õæÌ}1cpóff—ÎÝÿ˜ÁÖC4Ô%±K/oTWUáôÉã8°Μ:޲ÒÁ×?°Gyÿ;qü8êëë1~„v· ’Ì['[&ýw``ëó°’2‘˜ôàC‰D*5÷Ëž½JÿÛ±kw»mÿéêêêpùÒ%x{ßËbsvv†¶Žr²³qçÎ¥m++*”˜ˆð¡áŠÌŒ äææöÖÐÿvúõëGG',_¾šššØ²ùkܾuK¶^[G“|b±¯_/ ®@UU¾Ý²0zÌØ.eæÃ¾ÿ~«,¸b±Û·ý„¢¢" žžž\»ßÛ§PR,ãצ2 îî }IË`¦¥¦*´‹‰AuU•Ür/oo¸¸º"77_õ¥\ùÈœìlüúËÏ5z´ª‡MDDDDDDDDDÿBÌ`ë)M% …jB\ˆ<‹º:É\Rw‹‹u£ÇN€¥•ôôôPQQÑ—#½¯UTT 2ò†"++«Õmƒ‚%A´„øxÙ²„ø8Ì_°~þþ …‰DJÛFž;‡ù bÂĉHJJByy9´´´Ú[ËÀ€T}C}›í–/[ÚæúŠØØøùûÃÛÇG–]è--ÙJöšÔ¹sg±øÅ—`k;;¶oïñ±þÝèèèà—={å–ådgcé’×ä‚k0xð`hjjâFzºÒ åíÛ·QUU…Aƒµùo––\\]ÑØØ¨4À,‰p#=¦¦¦°wp@rR’ÜzmxzzÂÊÊ ZZZ$ÏÔ××C__b±X¶½t¾¹'ž\„ŸwïFVÖM¹õ-IK^¼­t¾¹””€£££êODDDDDDDDDÿ: °õQÓ Ü¼¼;²àšTUe%JKJ`dl cSØzØáC‡0|øŒ?ß4eë´dai‰¢¡¡ÉÉ÷nügff¢¸¸&&&pwwGrr²Òöùùù¸ž–'gçÏöúʪÌ}$.6x ðñ½—Á&“-6¶í[tTžk€ºº:"#ÿ}ó¯566âêÕ«]]] 8ýml°òõ7°rù2¹Ï33s€ƒ££BP®%]]]”••ujL&¦¦²yÜ~øi[›Û¶Ì`‹˜2sæÎE¿~ýZm£®®Žúú{ÁçÿmýÖVo $4!¡¡¨¬¬DFÆ $%&âè‘#(//—kon.9S1¥Ãc#"""""""""R†¶RS#™[­ººª•õՌۼ¡LÝ#5%7ofbøðáøñÇ”nÜT²¡¡¯.‘ÏSo*÷Üj€ >ß´ –¸rùr7üþ–ŸŸÛ·oÃÖÖ¶¶¶(*.†“³3jjjpõÊ•6ÛVUUá·ß†F? Üù–‡¬©©Á[ÿySö³ þóÖÛ°²²Â£=†Ï7mRh“™‘èèè6÷Û¼lbgUVVâÏ?þhs›ìÛ·eÿ:l>ú(JKK±eó×HLHDii‰,“nÛŽrsXJݾ}¯¾ò2¼¼¼áåí 777¸»{ÀÓÓ &NÂÊåËQ\\¤Ð.:* ™™™­Ž­¼¼sF"""""""""úwa€­‡TTH²'44”Ð444 ÓåØH5GÆSO?ƒaÆ)]ØTRKK Ê· ÂÖï¾mµ[·²pëVë%(IQ\\,lmmáíヂ‚…BÄÄ\TZ¯¥+WÈ”ÊÎÎÆç›>Ã[o¯Âˆ‘£ðÛ¾}¸ÝÄ*jšs­øn1vïÚÙ¹Z¯¼(S\T±X 555ü¼{W›å›1r$`óW_!*J¾´¤±±±ÒàšT}}=ââbe%EÍÍÍñò«¯bð`7LŽˆÀÿ¾ß*ÛV:÷\JÊ5ü¶o_‡ÆFDDDDDDDDDÔµ¾Àýª ?`jjAÓ|lRB¡ú†€òN–c#Õœ>u 5557~‚Â:]]]¸¹¹ž{æiÌœ>Mî¿ysç ¾¾–Me$ûZÿþýagoÛúz(]# ŒøøøÊæÈŠ‹ëË!ýc%'%!>.3ž%[~íÚUÔÕÕÁÝÝúúúÚwiY)´Ù¾¦¦©))ÐÖÖ–½–ah(ù,̽£˜‰8$$D¥qàýû–––rë’$û¢Ú>‰ˆˆˆˆˆˆˆˆˆ”a€­‡¢¼¼ ºzzpì&·n°»úõ뇪ÊJ¥%̨ûUWWãÌéÓpppP˜cÉß?B¡·oÝBAABÛÚÚZ\¾| Ô”éÖ—^yõ5¬[¿ÿ}ë­¾J—]½zÕUUððô„¯Ÿ >®íùרu;w섊þ66$å4<---,_±rm¼½}0z̘V÷{+K’™ªðÀ@s{~ýðüâàîî¡°~àÀxxÖ,¹e¹9’ÀÚÈ‘£ä–d‡Ùsæ¶Ú×”©SeÁ9)555„……rr²åÖ%%%ázZ\\]ñÔÓÏ@KKKn½¦–†//¯Vû$"""""""""’b‰È"‹‘ƒ°aÃá:Ø6¶PQ^}}èêéA$!>îb‡Ë¨Q×>|cÇSHËCÆÅ·ž9 __?cϯ¿vËx>\½¦ÕuGŽƉãÇ»¥ŸÎëêêðΪ·ñØãÃÃÝCvŠŠŠäl€$‹íÊåK˜ôàCìæ†àਪ¬DAAΟ?¨ çå¶¿‘žŽË—aî#ÀÙÙ666¸s'Û·ý„ý¿ÿŽïøQ阾þêKx{ûÀÞÁAAÁhllDAA>~Þ½þ± m °|éLœ4 !!¡prv†ºº:Š‹‹‘˜€˜˜‹¸pþ¼’Þˆˆˆˆˆˆˆˆˆˆä œÜ|˜BEDDDDDDDDDDDDÔAœƒˆˆˆˆˆˆˆˆˆˆˆˆH °©€6"""""""""""""0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆTÀ‘ `#""""""""""""RlDDDDDDDDDDDDD*`€ˆˆˆˆˆˆˆˆˆˆˆˆH °©€6"""""""""""""0ÀFDDDDDDDDDDDD¤؈ˆˆˆˆˆˆˆˆˆˆˆˆTÀ‘ `#""""""""""""RlDDDDDDDDDDDDD*`€ˆˆˆˆˆˆˆˆˆˆˆˆH °©€6"""""""""""""¨÷õîWCS³v·;{úŠ‹ŠzaDÿN¡¡a5f ¡­­»ÅŸqãŽÿõââb‹žz&NüôãØ·w¯Ü>>/½ü àbt4Ö¬þP¡+++lúâK@jJ Þx}¥Òñ|´vñù¦Ïpâøq…õ#GÆó/¼€êêj¼õŸÿ #ãà›ï¶ÂÈȨÍcÝ·w~úñÇ6·iÏî_~…šš|ܽ¦¦9ÙÙˆŒŒÄŸìG}}½l½=Ö­ß€ÆÆFÌš9CaáC‡âÕ×– 33K_{U¶¼ù9mͶŸ~ÄÞ={d?;::aÍÚµ¨­©Á¼Gævèx¤çQYQ;wî 11‡@III‡öÓ–Y³ç`ÖìÙÞþ䉈‰¹ˆ¥Ë–£¤¤‹Ÿ555 Û©©©aÃ'akk‹ï·nÅû¼ºd ÂÇÊm[__¢¢"\¾”Œßû ÙÙÙr땵i)-5¯¯\Ñáãè.o½½ ^ÞÞrËjjjp'711±ÿ÷ßQYYÙå~ñúÿ'·¬åû’ˆˆˆˆˆˆˆˆˆ¨£`ë!ee¥­®SWW‡¡D (+m};ê<¡PˆW^} ¡aa€Û·n!+ë&ÌLÍ OO<þèB…vaáClaaáíö,û·³‹ Qªâk†çžuµµøà½weÁµæ²²²PU¥<ØŸŸ¯RmIKM•}bŠ·þó&jkk»´ÿÒÒR\»v €®ƒn¤§£®¾PXXصƒhæö­[(--…¾¾>áìâ‚É“'cãÆOÕ¥}ÈŽEÊÌÌfff(/+CvŽ|°+''ΟǥKÉðôôÂÔiÓ°sÇ…ýŽ3¶¶¶ÈÉÎÆÁ*¬ÏÏËC^^ÀÀÐýûÛ`ô˜±x`ø¬ùðC$$Ä+k! ”Ç­¬[>æž““ƒ¢ÂB@ €¹™ÙÙÁÎÞÃGŒÄ›o¼Ž¢.>ˆ›“ƒÝ»v dAu"""""""""¢Î`€­‡$)¹¹-åèä /#ääd£¡¡¡Gõï±ðÑdžü¼<¬[ûnܸ¬8h¦LªÐæzZœœaem;¹¹]]]øúù!-5Î..­ö$ °åååÁÒÒ8þ×_¯¿^yõ5ˆD"¬Y½×®]SºÝ·ßlÁåK—:¼ßÎúú믙‘!ûÙ«Þ}NNN˜8i’BRU‰ HLHhhh`Ǯ݀ ÖËÎ}wúí·}²ŒAmL: 3fÎÄ’¥Ë°rùr¥ÁÌŽ:þ×_ ¯µ4«-)9 ¯_¯´ÝÖo¿ÃÚõë1yr:„»wïÊÖijiaÖì9€ï·n…H$RhòäIìÞµSö³‰‰ ^[²ƒÝÜðüâðü³Ï*|¾ÿë/¹6'GÂì—ýìâêŠ×ßø?˜››ã±ÇŸÀúuk»´ÿœœÙ±ÛØØ0ÀFDDDDDDDDD]Â9ØúÀÀAö€[73ûv ÷©þýûcÒƒB,ã£5«å‚kuó&>Û¸Q¡Ý…  ‰ä2Ö‚‡ ºº:ÎGF¶ÚŸ®žŒššüò³$PÔáñº{x`éòå€ ë×!))±Ãm{K~~>Ž>ðöñéãÑtMuUvl߆CB(bþ‚}2Ž›73ñ×±£ÐÔÒœ¹ò%/#""`llŒø¸8Y)ÓöãÓO%ïkSYVà?UjJ ~Þ-É8 ‚P(ìãÝà ¶^flb}TUV¶ZªºfÄÈQHHˆGfff‡Û•——ãRr2ÂÂñç×_HJFÞ¼™©P毹€€…BÄÅÆ"æâEˆÅbøøø@CCCn¾2eœœñúÿ |úÉ'¸Ýáñö¶Š IiJ-M­>I÷8xàOL˜8Þ>>066–Ë ë-Û·oGXøPŒ5ìÿ·neÁÐÐS¦B$aëÖïTÚ_~^JJJ`dd \î¦qjkkÃÜÜPT\ŒÊŠŠnÚsÛÒÒÒH²utuQ^V&·ÞÒÒӦπ·LLLP]]+W.ãçÝ»å20»J[G“'G $4VVVhllDfFöïÿQ.(loff†¡Ã€ŸŸ¬¬­ahhˆêêjÜHOÇÑ£GpáüùVû™8q‡†ÃÜÜB55Ü-)AZj*Ž9‚+W”¿¢î˜<9.®®ÐÓÓÃݻň¹ƒŸwïR¹T-u 3ØzÙ iöZÖÍ>ÉýËÝÜ”¤rÛsçÎÂÎÎýml ¯¯oooDž;×f› ¦òññq(//GúõëÐÔÒ‚··w›í„7ÿó_hkkcó×_áÌ™Ó*·79::rssúx$Ý#;;•prrî“1”—•áçÝ»¡¦¦†ù %sΚ=ÚÚÚ8xàOäd·Ømf¿~ЭågÝ=<°á“ØðÉF„„„vÛ~Û£¡®¨¯¯GEy¹Ü:oo¬ÿøŒ; ˆ‹EA~>† Á‡«×À«ë¯£ÌÌÌðÑÚu˜5{6 œœŒôôt8:9aÙò˜1s¦B›°ð¡˜¿`ˆììÛˆŽŠBÖÍ›ððôÄÒe˱ðÑGÚ…B¼óî»xdÞ<èë 111±1¨¨¨@Xx8†¡t|Ó¦OÇ;ヌÀ  äç#.6 "L˜8«?Z “n9DDDDDDDDD$l½H(TGÛ`ëI66¶€ÜNÌå…gž}aaá()¹ ¡PˆÈsçÐ߯FéöB¡¾¾¾€„xɼ{qqqprvF`P0bc•—÷³²²Æ¼ù  §§‡“'Nàè‘#*µ7!$4#GBCCüy ¯‡Õmò òa¯§K+«>ÃÁbܸqÀØqã1fìX”••a÷®]*ïËÛÛÚ::€[YYÝ=Ô^' ’E]¸±X,[ndd„%K—BKK ›¿þ G–­ Æò•+ñâK/ãùgŸér ñµ%KammÇcËæ¯QWWèocƒ·W½ƒ9sAÌÅÜlVò÷ffÞyûm$''ÉÛ«?Z‹ˆ)Sqêä)¹6þþ°·wÀôt¼ñúJ¹q›˜˜¢ÿþ cóõõüù PYQ>x)Ms7 Ì_°S¦NÅO.ºµué‘"Øz‘í¨««£° UU•}=œû–NS€¡ª²Jå¶•HJLDøÐp”Ü-AfFrss[ °yzyA[G·oßF~~>I&Û¬Ù³@ wƒ]júŒ€!!!صs Ú/ºêw[]÷ÆÊHMMíÈa¶kÝú Ëâ±kçN¤§_ï–>þjªkÜ{Ïô‘H„ï·nÅo¾‰gž}°cû6TUuüý«o`ooo<þÄ“€´ÔT¥åQgÍžY³g+ÝǶŸ~ÄÞ={T?€`llŒ!!!˜2u*ÒRSñ}‹R™&N‚nSpºyp .^ŒÆùÈH„…‡Ã? ÑQQ‡—·7\\]‘››‹¯¿úR.è•“_ùO=ý F­ß}+[—˜¨|Åüü|œ>uMŽ€»‡»\€ÍÈØpãÆ … `qqŠ‹‹ö7óá‡ß¿U\±XŒíÛ~ÂÐaÃ0$$zzz¨è¥²žDDDDDDDDDÿ °õ¢vv€[Y™}:Žû4p%†b`«#Î;‹Å/¾[Ûر}{›ÛJËC&ÄÇÉ–]OKCyy9LLLààà¨4 %°o[ |èP<¿øE¼óö[JƒqÍݾ}Õ­^ª›‚EÝ!åÚ5T×TC Ðߺ?<=½P:¡YYY¨­é¾¾úRgß#Ý-.. ñðõõCÖÍ›8vôh»mZ –ݾ}Ö¯SÚ¦¸¸E…Š(.*nµ¯Ø˜Ìœ>­Ý1uÅcO<ÇžxBö³X,Ư¿üŒÝ»v¡±±Qn[iÖèùó‘J÷•š’‚°ðp888v)Àæã#éçâÅh¥™p)))î•OmÉÎÎŽNÎ044„ººäëÖv€$‹ÙÀÀPnÛÌLÉœqááá¸|ùbcbÚ ²jiiÁÅÕJç‰D¸‘žSSSØ;8tªd.µŽ¶^¢¯oS444tj^%긪ª*èééu:+):* Ï5@]]‘‘mÏ¿ˆ»`‹ÅHLHÀÐaä4ÀvòÄ üôã000€·¼¼¼0~Â:x°Íþ¶lþ—/]êÄQ©fË–ÍÈÌÈý¬££ƒ'-Âð#¡¥­µkÖHV´”éèv½L[[TÊë)™ðõõÃÍ›7Û ´@~^òòòuõõ(.*ÂåË—uáêëë•¶9vôvïÚÙ­ãî.wîÜAQQ„B5X[÷‡¡¡!f>< åååøó?ä¶537¼þÆÿµ¹O}}½.ɼ©Ÿˆˆ)ˆˆ˜ÒêvzzòýXXZâÕW_ƒ³‹K«m¤7©´ÔwФ IDATTìݳS§MÃ˯¼ ±XŒììl¤¥¦âìÙ3HLHÛÞÄÔjj’iTøi[›ÇÑr|DDDDDDDDDÔu °õiöZNömˆD]›ˆÚ–}û6\†µµâœEQUU…wÞ~ý4p§yÜììíafÖt~ê4Lzh²lUÓœ^AÁعc‡BÛË—%A²²²2lýî[¼ôò+X°`!âãqçÎN»'UUUá«/¿DXøP kkkäææ*dµFÔ(êávŽ…¹ ïoxÎÛsòÿÙ»ï°(®5€Ã¿©¢ `¡ˆÒAº ¢‚5Šc‹ÆnÊ51ÉMbŠ%1Æ’DMÓÜTSL³Æ^b¬±½ ¢i0JµÐaï+«ë.bÌ÷>ËÌ93gvwfvæ›óÝ»Ú`YulÙ´‰ÿTc6œAO>Éè1OsìØ1ûâÆT˜ ¶¦Ò™>tHgÊÍ2ׯ_S¿ÖÓÓcÊ»S±··'""œ?6l --MÝótÀÀŒ=†[m5,Y¼ˆm§uë6x¶j…—·7]»u£k·n¬\±‚å¿kKnÞ¼©€¼Û¥‹+·¡B!„B!„BˆJ“Û §§‡}ó\¸cÌQ;Ξ=‹»‡Þ>ÞlX¿®ZË8s&úžeÕ¯ýüüt–iÙ²%ÖÖIO/|µð={ ¥uë6ü÷•W™öÞÔJõ`zÐŠŠŠ¸rå vvvØÛ7W òòÕw\×xsúúúêr[;;ê›™¡T*9w.¾®›#îPRR²¥KðôôijU+† Îÿ>¿=.`zz:ì çܹªÑ*»w¥§§Ãúu•;–¸¸¸`ooOrr2_þïZûDÓ¦Í*¬ùòe6mú“M›þD¡PЭ{wƽøOÌÆpóÖXj™(•JôôôX¹bùCyÌB!„B!„âQ¦W× ø7hfc‹‘‘7oÞ ##½®›óÈÛ½k'J¥ÿš7w¨µõ”¿öÕ—_2xÐ@­1gÏ·ÓHVä‡ï¾#//ÏV­xüñ¾µÖæû¡§§‡¥¥%  €dffª_[YYkÕ)K±—~õáûÞ÷êÝ€¨¨H²²²ê¸57SSZ::ÒÒÑñ¥T*•üöÛ¯t ÁÎÎN=/ò”*]b»àöÕZvNN 4¨°œz=í‚+½lss @Õ+òî —­[WzYJ¥’ýÅÅ‹ÑÓÓ£±õí},??Ÿ¸ØXLLLÔcÅ !„B!„B! °=-¸’RÇ-ùw¸xñ"Û¶nEOOIo¿C‹ómíìxù¿¯Ü×:,--qtrº5ÞÚ eŽ; ÜÄU$==%‹0bÔ(lm«—Þ²¶èëë3zÌÓ˜˜˜PPP@ll   ´?~ €Q£GkŒ+ekkKï>päÈáßèr˜˜˜0løpz÷îCII ‹.¬ë&=ôZµjÅgsçñÙÜyÕjUǹøxŽ=ŠB¡`ð§ÔÓ·lÞLnn.ýú÷§GÏž(îʷبQ#D£Ft.÷ædeeaee…[ã¤EFFr.>7wwžaÆÆÆóŒéܹ >>>êiii©x{{«Ì Ú‡žyö9¬­µÑþþøøøhm‹““¶¶¶såÊykV¯àåWþK«V^ZËtpp`ÈSOiMB!„B!„BÜ?IYËLLLiܤ J¥’ $Àö üúËÏXZYÄÜyŸ“’’BNv6VÖÖØÛÛsýúuø¦úËoˆB¡ 1!ììleŽ;ÆÈQ£ñòöÆÄÄäži·nÙBHH(žž¼òêk¼;å­0Ï¿0N=–ÓÝâÏÅóó‚ÕÛ »Œ÷"ùùù€*ˆ`oo©©)J¥’?þÀÍ›7ÕeûåÜÜÜ ÅÏÏäädŒŒqvvF__ŸösøÐ!åûû0tØ0€Â„ )**TA”={vkµÍÐȈ9}\nÛ;ƪ•+4¦õï?€.]ºbÖÀ {ûæèëëSPPÀW_~AbbbÕÞœ°ÇzƒS'Oª¿ïß|ý•z¿S*•ÌýìS¦Í˜IX¯^tèØ‘ä¤$rrr°²²ÂÙÅCCCæû-DEªÿûiß¾Ÿñ%‘§NQPX€§§' 4d÷®]téÚU«=.®. >‚œœÎ;Çë×hdi‰——7úúú,]²˜Ü»öýcÇŽ±tɆÁû~HJJ2©©©èëéÓ²eKš4mJff+W¬ÐZŸB!„B!„âþH€­–5oÑ…BÁÕ«WÊ ŒˆšWTTÄÇsæÐ1$„nÝÃÉÑ{{{²³³8rø0;w츯巽5þÚ‰º{¯œOI!=ý*ÖÖñàÀþý.S©Tòí·ß0wÞ縹»Ó¯­qŸìííË­_ ¨ ®wôê),,$##ƒC²yó&4Ê^¾|™ o¾Á 'ÓºMZyyQTTDbB;wîà¯íÛµ… Íj¬£LKGGõëÇiÍU@NWÝ2—.]Òšfß¼9vööܼy“¤ÄD"#O±yÓ¦]jHKK+,-­tÎ+--}À­©œ¤¤D:D»à`ž<„¯¿ú€S'Oòæëãy¢_üðó÷§¸¸˜ŒôtvïÚÉáǹzW¯;mݲCC#zõêE»à`u wþ·ß¨Óž\½z•IÞ¢wŸ>·ÇÅÕ•zõê‘™™É©“'9zôï äýoÞ<âûÆÓ¥kWü(ÈÏçÌÙ3,_¶ __?Ð`;°?††FxyyáììLýúõÉÉÉæäÉ“lücQ‘‘:·cÍêUœ‰>MŸÇûâáéIPPsroÞäêÕ«8p€CË2 !„B!„B!ªOáâé§¼w1!„B!„B!„B!ÈlB!„B!„B!„BT‰Ø„B!„B!„B!„¨ ° !„B!„B!„BQõêºBˆšõù_T¹Î3g’™™Y ­ùçpqq`¾Z¥:™™|ðþÌZj‘B!„B!„Bˆ‡•ÂÅÓOY×B!„B!„B!„âŸBRD !„B!„B!„BQ`B!„B!„B!„¢ $À&„B!„B!„B!DH€M!„B!„B!„Bˆ*›B!„B!„B!„U 6!„B!„B!„B!ª@lB!„B!„B!„BTØ„B!„B!„B!„¨ ° !„B!„B!„BQ`B!„B!„B!„¢ $À&„B!„B!„B!DH€M!„B!„B!„Bˆ*›B!„B!„B!„U 6!„B!„B!„B!ª ^]7àQgßÜ-ihn¾¾>……d¤§õœœºnÞ#G__Ÿå+W‘‘ÁKã^ ´´T£Ì/¿-¤AƒŒõ.]º@§Îymüë.{ÉâE¬]³sss¾üúêׯÏ3gpêÔ)­ò¾¾~L›1ƒ›7n0þµWÉÎήòö<5tO Zéò»wíâ믾ԚþÝ?bmmMqq1Ï>=†¼¼<­2žž|8k¶Æ´’’rrr8Ï–Í›‰ŒÔÞÎ2666ôëß_?,--).*"1)‰ð=»Ù½k%%%ê²=ÃÂxaÜ‹÷ÜžáCŸ¢¨¨ˆUkÖÞ³ì^~qW®\©Rº6jô ÈãÇsáÂy\\\øè“OYþû2V®X¡.7ùíw âý3*ü¥Ü¶(•J­÷ÀÃÀÄÄD 4斻̇•›»¹¹¹\¼x@ýŒ‹­Ëf P(˜8i®nn$&&r:ê4%%%dff–[§,=jÄpòóó`kÿyŠ‹‹Y±|¹úïþýûcdl|ÏzÛ·o£ÑQUЪ}‡ö4oîPkm¬ŽþÐ¥kWàö¾|/­[óâK/«ÿ®l=!„B!„B!4 °Õs Z:9SZZʾðÝdeݾíí뇓³+>~ìØ¶¥[ùhS*•ô «T€íÔÉ“œ:y–-W‘æÍ›Ëå´4u¶mÝB×nÝpqq!,¬›6ý©ž×çñDZ··çÌ™hvîø«ÚÛ°sÇvîØ¡1­¬W[dT$ŸÏ{Ïe´ àï¿ÿ¦iÓ¦´ ¬0ÀVPPÀÌÓÕòŸ±céþX†ÆáC9þ¼z¾‹‹ ãßx…BÁÏ °eËfu¯Á 0ä©¡èééÎF›˜”Äô÷¦Þs¦N™¢5­¬WÛŸÏS÷Dü§Ò××ÇÙÙ…˜³gÕ7ww”J%qññuÜ:U°ÚÕÍÔÔT&Oœ A¦ °ý®þ»gXXålÛ¶©_ÛÙÛ=t¶¤¤DÒÓÓ‰!9)‰úùžu232Ø´éObcbˆ‰å­ pus{­B!„B!„¢jd ¶ZbeՀ̌tà@­æõë›a`høÀÛöo‰ŸŸ?M›6­•å+•J~øþ;”J%C‡ £Aƒ€*ÅÙSO ¥¨¨ˆïæÏ¯ó`D`P ‹. uë6å¼t),,dÁ?’B¡ 0(Hcþsÿ‹žžë×­eÓ¦?5Rr^¿~ŸZÀ¶­H®H‹–-122"öŽÞjîî\8ž¼ÜÜ:l™ŠÕ­W®ü]çßgñÏñçÆü¾l)'ŽçÆ•ªsêÔ)~^°€}{÷’ž~µ–[(„B!„B!DõI¶ZRZªJ¨óV´¢¬L)ÅEE¬Mÿ6Û¶nÁÏÏ=ÃX¼ha­¬#1!­[¶Ð«wo† Î?üÀè1c015å÷eËH­ãžUVVV8::qãÆ :HJJ2-Z´ÄÝógÎTz9EEE$%%@£F–êéööö¸¹»SZZʆõëË­Ÿû‰ Í›7àæÍ›dddÔq‹nsww >N`³´´ÂÚÚšíÇ•[G¡§à‰~ý ë…uãÆddd¾‡Õ«VQt×qeê´iøû0sú4\\]éÑ£',-ÉHOg÷î]¬[»–ââb:½ûôá?cŸ×˜æï 1Þc°YYYñý ´Úy÷Øb¿/[ʪ•+µÊu¬/½ü2[6ofáo¿òäà!„„†biiÉõë×ˆŽŽæë/¿T§b ¢m[ÕX]–VVÔ«WôôtŽ?ƺ5k*LËjmݘ~ýûãïïuãÆ’ššJDøvîÜIÁ])-MLMyâ‰~·oO³fÍ(--%9)‰?þØÀ¡ƒË]ÏÃÆÇ×—é3fsö,SßÕî 0þ7 íÄ·ß|ÍÎ;°°°`ÁÏ¿pìèQ233éÔ¹3ÙÙÙüðÝ| yî?c±°°àÐÁƒ|ýÕ—©r…B!„B!„x”I€­–¤_½‚R©ÄÊÊšF––dÝ«H¡Pàì¢Jw•’œ$½AjÑé¨(R/]¢{÷î,ÿ}™VС¦,]²˜àöíéÑ3Œ¤¤$:uîÂÅ X·vM­¬¯*ÊÒCž:u’ÒÒRN?A‹- ¬R€ ÀÀ@u¸¸sÜ­V^^¤¤$síÚµjuí022bÞÿ¾ "<œ/þ÷y¶gæâuëý+óî{Ó4þîÑ3Œ=ÃûܳA£¡C‡áâêJtt4çÏŸÇËÛ›ÁCžÂÕÍߟ©óØ2|ÄHœ]\8Mrr2^ÞÞ >GG'>ýäc²ñññêqÁ7¶¦k·î\¾|™ð={ÔerrrÔ¯sóò4Æ0p †††¬Y½Z#xw&ºâïB¡àÝ©ïáåíÍÕ«WIM½D³¦Í íÄ÷óç«8¯¿þ†FF¤¤¤‰ž¾-èÛ÷ BBB™úîé]}|}™4i2&¦¦¤§§sâÄq(ptrâ?cŸçÊ•+;zT]ÞÚÚšé3ßÇÆÆ†ììl¢¢¢066ÆÍ͉“&³léV¯ZUá6=,NGE‘••…»‡ÖÖÖãJ‚*l``EEE~þ„têJVf………4hØccÎÅs6:ª®›ùÈÛ¾}O?ó,ÁÁ퉈¯•uäææòÛ¯¿0þõ7xñ¥—Q*•ÌŸÿ­V ºP–òÄñãªÿOgÀÀ´ báo¿Uz9õÍÌptt¢´´”Çoiggg@jjj ¶úßáø±£êP×nÝÈÎÎâÄñøøøÐ¤iSvïÚ¥(jÔwvqკ3ˆŠRGÌÍÍ™5{~~~tíÖMkì¾²:3gLçLt4 Jg:{ÎG´ &¸}{ Ê¹øxÎÝJgëáéy+À–¦1VØòrs5æõîÓçV€mùwõ«H»à` ˜S^^žÖ2Ë\¼uÓ¾¦äçç3xÐÀ]fMðñõ *2€FaggÇöm[+¬w:J»lT”jÎÎ.( ­4‘Q:ꜾUÇÅÕµê¯ÉII•N_jbjŠ··7Íš5ÃØØ…BõÌHQQ 4Ðzüüüøë¯í•Z¾ŸŸ?GŽÖÙ#56V5fž³³s¥–÷0HH8Gjj*NÎÎ4³±Q÷¢411¡u›6äåæj¤È,“—›wûuž*Ø[X/))¡¨¨“°B!„B!„B<$ÀVKlíìquóàïËiœŽ<©žž‘žÎ¡ƒûèöXnîž\HI!7÷f¶ôÑ·uË:wîBϰ^,øñ‡_~}33žyî9”J%ë×­cÀÀƒfÍšñô3ÏðÍ×_«ç]ºt €f66ËˆŠŒ$*2ccc/]V³÷X±j5zzšz—Üõ>½5a"—/_æ•—_Ò^È=‚·º‚»ºªÔeX—k׮ݳLHh(cž~šœœ~üá{NÔ‰?_¹«¤²4ƒÆwõàª æææ¤éè!Ü.8¸Üz‘‘§x¬GÏJ­'ò”*½o»vå/³6\»T- ’WFεªÕ)ë©Ö1$”àöíÑ××gïÞ´B!„B!„BˆòI€­–\NS#ÔÒÑ KËÛ=M ­¼066¡¸¸˜«Wn, qòòòˆÇÉÉ©Üñ’ªjà AØÛÛÇ_Û·kÌ[¶tÉ­€^ŽŽº{Õ¦¶mU³ãw¿Væø±£øûP¯’ã&ý¾L•°cH¶vvêé¿þò3J¥’¡Ã†Ñ©sg:ַƼzñÙÜy|6w#GªÕu½þÆ›|6wÓ¦O¯°œ——7………ÄÅŪþöVÀNŸ>}ÏutèБV­¼Ô›™™1tè0vîØ¡³NHh'<<<Ô7hØÁC†Ôx:Óô«WU/Ïš––ª ¬uíÚMcz‹-:¬ü^c[6o&77 „B¡™Ô°uë6¸º¹©ÿŽŒŒä\|5jĤÉo«ÿnîàÀðá#èÛ÷ N:ÅïË–Öh[…B!„B!„¨* °Õ’Ò’ì §¥£3v͛Ӱ¡9úúúzé"çâcÉÎʪëfþ«$'%§ÑC¥LCó†:§·ttT¿>|øúõ¸_ÂÀÀ€M›þ$))Qçúþذž={âæîN×nÝÊíUTÓU½×ââbËÿM©TrâÄq:wîB`P`¥lË_FÛÀ@BC;±rÅ .§©zíøk;ñq±ê€G«V^ªžY±±ìÙ³›];wê 0š˜˜è|ßÕóMM*Õ®"gggLLLˆŽ¾Ý[ÍËÛ›¤ÄÄJe·ü÷e8::Ò£gVVVdfd°eófÖ¬^Un0wɒŸ¹¹Ñý±XZZ’žžÎŸÿ`ÝÚµ5Þºe ††tíÞàöí100 ??ÿ¾l‰ Lž4‘á#Fàêꆗ/§±tÉbþذ_.*·î©“'yóõñ<ѯ?~þþ´ ¤°°´´4~ýå­àæÕ«W™4á-z÷éCpp{\\]©W¯™™™œ:y’£GpðÀûÚž»%''3gÖ,† Ž··º—iøž=åØ yæ žyöY¼Zy©Ç^ÌÈÈÐ`ÇÇ×€ˆˆˆÝ€†ææ:÷ï;Ç…4·0טg`` ³N3šÙØpåê•n©B!„B!„U§pñô{0Ýj„BÔ‰©Ó¦áïÀôiï]‰ô“B!„B!„B!*&c° !„B!„B!„BQ`B!„B!„B!„¢ $À&„B!„B!„B!DÈlB<...ü÷ÕW«T'3#“ÞŸYK-B!„B!„B!DuI€M!„B!„B!„Bˆ*‘B!„B!„B!„BTØ„B!„B!„B!„¨ ° !„B!„B!„BQ`B!„B!„B!„¢ $À&„B!„B!„B!DH€M!„B!„B!„Bˆ*›B!„B!„B!„U 6!„B!„B!„B!ª@lB!„B!„B!„BTØ„B!„B!„B!„¨ ° !„B!„B!„BQ`B!„B!„B!„¢ $À&„B!„B!„B!DH€M!„B!„B!„Bˆ*›B!„B!„B!„UP¯®ð(S(´ptÂÁ¡ 4…‚›7®sáüyâQ*•uÝÄGÒŠU«ÑÓ»;V*•ܼy“””"Â÷°sÇJKK5ê,\¼SSS¦¾;…˜³g5æYXX°àç_xzô(nÞ¼ À‡³gãáá ÀøW_áÒ¥Kõ&LœDpûöüðýwlÛº•™|ˆ——W¥·ã›¯¿b×Î|ýí|š5kVnÙ¢¢"†} € ò˯¿››ËsÏc&>¾¾ÓŠŠŠ¸zõ*§Nždíš5dffhÕ[²tFÆÆLž8‘„„sn€_|õõ=ËÍúà}Nœ8qÏrÿ&}û>Á3Ï=ÇêU«X¶tI]7GËà!C6|ëÖ®añ¢EuÝœû6ùíw âý3ˆŒ÷,ÙÙÙuÝœ:aeeÅ÷?. õÒ%^{õ•ºnÎCçQ?‡=HÕ9ïÝëXgllÌâ¥Ë´¦ßy½Z—Z::òÙÜyÄÄœeê”)uÝœZWvöù¼¹ìÛ»·®›óHøå·…4hÐ@çõ½xø¸¹¹1û£‰ŽŽfú{Sëº95bê´iøû0}Ú{DŸ>]×ÍyhÕåùH>#ñ°[-jÝ6;ûæ”––’•Eii ,ñòñ¥q“&:°O‚lµ(>.Žüü|êÕ«G3¼¼¼ðòò"¸}{æÌšEIII­«gX/~ùù§ ËœOIA__³Óh ‡˜˜šrùòe²³³4æÝy³#!áœz~ãÆM°²²âÚµk¤¦ª‚zEEš´2¦¦¦øpôÈõ4'''up­<éé餧_Õ9ïÂù Ö½—˜˜³ZÓÊ•‰‰‰hÌ+,,Ôø;/7—”ó)å.ÿî}ê™gŸ¥ïý¸|ù2çSR0­oЇ‡^^^˜šš¨oäßÏgT]YYY\¼xSSìììéFž=Y»f5K—h@ÒÒÒÔïaýúõiÞÜââb΋W—ÉÍËÓ¨ckgÇû|ˆ……ùùùœ?ŸB~~666tí֮ݺ1üpù.·_–`+“ššJFzºº6¶¶ôîÓ‡ÐP¦NyG+}?t}ŸÊÜxn.ñ(6|ý ==ƒ¨×Ew·ïÔºuÆ¿þùùùœügìólذž…¿þZ×͹oò{¦öÝïùH>#ñ([-±±µÃξ9EEEìØCNŽêF¼‰©):v¢IÓf8´hIJrR·ôÑõý÷ß©{f<Ö£/¾ô2þþ<Ö£'[·l®‘õ(•JºtíÊ’Å‹´‚AwúiÁZÓÊzLmX¿Žm[·–[÷ó¹sÕ¯GÍ€ƒ8uò$_üïórëdff¢T*éС£F€­CÇòóóI¿zûæÍuÖݹcG¹Oºß/]O0–õjûâóy÷ ¾$&%Uú‰¨€€ú>Ñ›7oòñGs8­žgbbBHh'êÕÓWO»ŸÏ¨ºN<É×_} €žžCBxaÜ‹ zr0™™™lÙ¬ù=]·v ëÖ®Qoß»ïMãÚµœ Ÿ }õÕ×°°°`×ÎüüóOäåæªç99;kô¤»›¡¡!>¾¾qýúuš7w iÓ¦üýwù7¶mÙÂÆ¨ÿ611áõ7Þ¤MÛ¶<óìsÌúðƒŠß”*ø7<+ÄÃ*¸}{¦¼ý¶ÎÞ©Õùþ»ùD„‡×ZÛfVVV¸º¹‘ššÊä‰jõa«ÈS‘þL\\l¹eRSSÕç|;;»‡>À¶ð·_Ñ×Ó¿wA!„xT÷¼w¯cê†æí뵞aa`BÔ™+W®ðëÏ?“‘Q¹k‚‚m[·ròø .§¥ÕuSjuy>’ÏH<,d ¶ZbßÜ€äÄup T½oÎDGÐÒɹNÚöoõ×öí:t€vÁíjl¹‘‘‘Ô¯_ŸŽ!¡5¶Ìš¡äÀý©ÓvtèØ‘cGV |T„têÀêU«4‚kyyylß¶•Í›6ÕEÓt*--%"<œo¿V¥?1r†††÷µÌ¦M›âêæÆÍ›7ùnþ·Á5€Ä„þ÷ù¼r{¯ùùùcddÄ™èhŽ;¨RnVE^^«W­ÀÇ×…BQ-B.žví‚iÔ¨Q-3|ÏÜÝÝ c×Î5¶Üš°ß>úö}‚€Ö­9|è®nn4iÒ„ß~ù…'®ëæÕ:ËF–\½r¥Ž[R5 ++‹Fľ}ÕÏãßÈRõdffT+-jY0íÄñã\½z•Çzô m`nÜX¥å”¥k¬W¯õêÕ«ó<úÍš5ÃÐÐâ’Rk0e%ÜÎ>sú4\\]éÑ£',-ÉHOg÷î]¬[»Vk\Ä2?,X€¥¥Ï>ó4vvv yj(...èé鑚z‰•+VhôHU(ôèÙ“nÝÃÞÞ===ÒÒR‰àÏTø>·iÓ†!O Å¡E òòò8yâ8K/)7XÒ´iSz_??,--ÉËËãÌ™hV®X¡Ñ[¸6˜˜š2uê{¸{x°~Ý:-üM=¯lŒ’§ÇŒf̘1µÃÈØ˜óçÏóÇúõåî?Ílløú›oÕã^uíÖ>÷ÁÖ֎‚ùé§ßSSS Dpûö4nܘ¢¢"’““ؼiö—1©ÐSðD¿~„…õºqc222ˆßÃêU«ÊýŒZyyñÄýpswÇÌÌŒ¬¬LŽ9ÊÊËÉÉÉ©æ;©ÍÇ×—~ýàêꊱ±1™™™?vŒU+Wh¥Á-Kßq'}}}­±5l ;Íþèc¿uEÓ¤iS ˆ·*˜WXHFF§OŸfãÆ?¸¢£'­B¡ K×ntïÞ‡-000àòå4öìÞÍÆ?þÐØ÷:uîÌkã_רïä쬵=ºÆg­ª²t;wò÷ÐX—®1؃‚hÛ6www,­¬¨W¯ééé?~ŒukÖèLU<|ÄH­s|Ec°=h&¦¦ôî݇Ž!iܸ úzzdegÇömÛ8sFó}}}–¯\¥µœŠÆ`»s<ÚòlÙ¼™?þ 5½¶÷½²ýHט¶,[¾‚ÜÜ\ÆŒ©U·:û„ŸŸO‚³“3Å%%DEFòë/¿èl[]ìºÆÜ,ë¡äða>þhŽÆ<…BAHh(aa½hfcCýúõÉÉÉáÂùóìß¿OçØ¸ …‚°^½èÖ nܸÁÞˆ¶mÝ¢³mºÆ>¾{œÂ¨ÈHfΘ^M× ç°[ë¨Æçú0«Îy¯:Ǻûñ ~g€*]ýÓO?C«V­@¡ ..ŽåË–ê|ø£:ç=¨ÞñÑÄÔ”'žèGpûö4kÖŒÒÒR’“’øã :xPçzšÙØ0bÄH||}122")1‘¥µ0®qu÷‰Ê~®eãÌ;z”ÌÌL:uîLvv6?|7CCžûÏX,,,8tð _õ¥Æudu>£êë@•åeÀÀt¬ÖÖÖääd³}Û6Ö¬^­óA¥êëªzV•÷û~TwŸpswçñ¾}ñððÄÜÜœk×®‘’œÌ_Û·søð!õû§kœ÷~ýúkdº)--å©ÁOj”ñõõcÚŒÓ*ƒíÃY³ñðôdæŒéDEFjÍ/;·¥§_å¥qãÔí³¶¶&$´4³±ÁÜÜœ¼¼<ؾ}ÐXÎýüžùpölõ&eî5¾WU¯Éïü-øá‡0rä(ƒ‚033#-5•eË–räðár×WY_~õ5±xÑB† žžkV¯æÈáCŒãMZ´hARb"óæÎÕ"¦¢ñç|ô1®nnLxëÍûºð ?£ªºsLÁ?üÀÓÏ>ƒ‡‡'ÊÒRbbbX¶t ‰‰‰:ëv¬/½ü2[6ofáo¿òäà!„„†biiÉõë×ˆŽŽæë/5«666 2__ÌÍ-¸qã1gϲzÕÊr×`mݘ~ýûãïïuãÆ’ššJDøvîÜIA~¾Fùª\+—©êµ[Ùz¥ßu•!¶Z¢¯¯Jç ë¢£¨èvÏ¡f ÈÛc` úÊgffÝ£dåååå²7"‚ÇzôÀÉɩƒ߃GzúU:v áð¡CtìØ‘¼¼<Ž?ö¯°]¹Xkß¡øÇŒy¨T*‰‹‹¥]»`\Ý\ï+ÀV\´µµ£eË–$''Wº®B¡ MÛ¶œ8qœÌÌLJJJhÕªõëׯҀµÎ·z즧_­óàÀ›oMÀÉÙ™ÌÌ ^;¶VÖ1|ÄHœ]\8Mrr2^ÞÞ >GG'>ýäã ëúûûóÊ«¯Q\\LZZfff899ÓÊËKãÂîµñ¯Ú©………DŸ>MqI1^^ÞŒ=ÿ>˜9Sg`ÕÇ×—AO>IrrÇŽÅÉÉ™Î]ºâåíÃ;“'‘•¥yŒôõõcÒÛocllLjj*ÇÃÚÚšví‚iݺ ³g}¨ó"©&Ô73cÚôé8;»°jå ~_¦=€2À»ïN¥yóæœ>}C##¼½½yã­·0·0gÓŸV¸ŽaÇ3xÈS\¿v´ÔT¬¬­ñó÷ÇÆÆF}sÒÔÔ”YsæÐ¼¹999?v SSSZyyѪ•«V®ä÷eKu.èÐa¸¸ºÍùóçñòöfð§pusçÃ÷gj›ÄÈQ£Q*•$œ;G\l,ÍèÕ»7mÚ¶åÝwÞ®‘ñºëуq/¾@ll YYY¸¸¸Ò«wo‚Úñî;ïpõêí‹­œœüúCžz ¥Rɪ•+5–{÷ƒCë×­ÅÐЀ.]ºÐ¤iSvüõ—Æ“†.jŽïÙÌÆ†O>ý SSSΧ¤pøÐ!ŒŒŒ°µ³£Ïãsî\¼ÖÍ2}}}&NšLÛÀ@ òó9wî…¸º¸2jô¼}|˜ýᇔ––’œ¢ÞsssÂzõ"++‹íÛ¶i,7ýjzåßÔrÄÇÇ«×Õ¸±5]»uçòåË„ïÙ£.£ëFÌ믿¡‘)))DEF¢§¯‡ƒC úö}‚P¦¾;E+%Êé¨Hõ~ïé鉯ï}·¿¦èëëóþàèèDff&§N¤´´”¦M›Ñ¡cG µ.ÒJKK5¾wƒ‡ AO¯â$…êtÊw j‡­ÅÅÚ碵ïUGuö‰ŽCxýÍ7ˆ9{–k×®áÙªïø¡ÎòrŸ¨®ÿŒ}ž^½{SŸÏéÓ§¹yó&Ö­ñððÀÆÖVçûØçŸ'¬Wo ‰ŒŒDO¡ WïÞ¸{xè\Ǧ?7bf¦ ŽtèØ{{{öFDššª.SQªìêø·ŸÃªó¹>̪sޫα®ºä±ÎÜÜ‚Y³fSPXÈÉS'iܸ ~~~xyy1ëô~?Vç¼W㣵µ5Óg¾ ÙÙÙDEEallŒ››'MfÙÒ%¬^¥ð´µ³cöœ033ãâ… \¸x‡L›>ƒ”*\cUFuö‰ê|®þ¤¤¤ƒ¯¯¯½þõôõ9}GG'B;uâÌ™hó@u>£2Õ9Ö}þ:tèÀ¹„n\¿Ž‹«+ÃGŒ¤¸¸˜õëÖi”½Ÿcú=©äu؃ڪû~8‘£F£P(HNN&.6Võww' ukFNþ­›ïGQŸ\]] hÝš¸ØXNž<©^žRYªµŽ¿ÿ¾¬>nYYYiéîއ§'!!¡:¯Cne…Ú¡qnéÐ1„Q£GsýÚ5’’“ˆ‰ÁÜÜ/ooüüýÙ°~ »ý`ÕýüžÙ¹c‘§TmëÔ¹³Ö7ºT÷šÜÀÀ€™3ß§‘¥%ÉII4nÒ‡-˜8i2S§¼S#(LLLô䓜‰Ž¦m` cž~š°^½HOO'9) 7wwFÍÿ>Ÿwß몊ýUW# >˜5‹üüâøñcZË÷ñõeҤɘ˜š’žžÎ‰ÇQ ÀÑɉÿŒ}ž+W®hŒ\Õkå²:U½vƒGïw]eH€­–bjZcygMLt¾µÏÛGu“é` w[ߺe õèAϰ^|7ÿÛ]öýP*•ìß¿Ÿž=U9ÛwèÈÑ£GŠÇƒ°cÇ_tëÞ;bckCÄžp"£"9Ÿ’¢qÒx•ÆšÞç…ŒŒ Nœ8A@@s>þ„=»wsüø1õ¶Š¸ººbaaÁ•+WÔcãÅÄÄàååE@ëÖ숸çúë›™áééɨ1cذný}mÏ?‰³‹ 3gLW§'µ°°`öœhLpûöZOÚÝéégžå X±üwu:WkëÆ4w¸=nbpûö„vêDvv6S§¼Ãå[©‡6lȳfãííÃã÷eÃí÷ÜÍÍß—-cÕÊ€ê‡Óo¾EpûöŒ3†¯¾¸ÝKÉ‚·&LÀØØ˜¾ÿNc,ÂÀÀ &½ý6¯¾6ž—_Wnϼêjذ!Óf̤eË–,Y¼ˆµktß0UoÍ×^}Eôðð`Æû0zÌÓ:x°Ü´Yòøã}ùâóÏÙ»÷öÅœ›»;ù·EÃGŒ¤ys¢££™=ëCõÓ`®nn̘ù>ȎÇIH8§µg>˜9ƒ¨(UŠhsssfÍžƒŸŸ]»ucçŽÛ½Ÿýý9j47oÜ`öìYÄÆÄªè£F¡ÿ€<÷Ÿ±|öé'Uy+µXYY©{T}òÑG9¢zJR__Ÿ×Æ¿NÇÆ>ÿsfÏR×QØn÷‚ìªsޫα®:ô±ÎÆÆ†ÇóÉÇ©¯Aû>Ñgž}–—^þ/¯¼ü’ÆuYuÎ{ÕùÍðæ[°±±a×ÎüøÃ÷êßÛ¶vv̘ù>Æàè‘£êc"ÀóÏ¿€™™[6oæ§?¢T*Q(¼0n=z†Ý÷{U¦:ûDu?×7n0åíÉ3köÜ=<Ô=‹Z´`ÞçÿÃÛÛGã¦wu>£2Õ9Öy¶jůW?<Û­{w^þï+<Þ·¯Ö¹ü~Žue*sö ÷£ê¼ßþþŒ=†üü|>ûäNž<¡žghhÈ€5XGŽV_ ôîÓ‡€Ö­‰‰¹çùÿï¿ÿV—qss»g€mÿ¾}<÷Ÿ±óÃ÷ßi:†t ü®ñ*S’“xÆ ¢¢"5ÚݤI>úäSúõÀžÝ{Ôûëýüž¹óœææîvÏàÍý\“·ttäØÑ£¼;å P(ü÷•WéÒµ+½û<^#6…BÁGs朔Ĉ‘#ôä`rrr˜þÞTôõõùþÇxûøÜ÷zªêA~F÷ÃÖÎŽcGòé'«Ï%ý `ô˜§yñåÿòæëãË­Û.8˜Â‚&Oœ¨>æèééªñÝå•W115eÃúu,Z¸PýïÓçqž;–ÿ¾ú*/{Ac˜ &Lœ„‰©)K/bÝÚµêz …‚ö:póÆ öTõ$Xâ¾ IDATZªwíö(þ®« ƒ­–”¥†´±µÓš×¼y õ벞n¢öèééÑÌÆ†±Ï¿€——;þú‹ýU£ëHJJäܹs„††bbjZ£Ë¾_û÷íÃØØ˜#FbeeÅþ}ûîYç©¡CYµf­Î z­.Ÿ——W¹m›:mšFÙ¸ØXæû y¹¹8::1æ™gølî<~ùm!¯¼úöÍ›—³–ºWö4™©iýû^Ö7_}ITT<Ö£“&¿ÍO¿üÊ'ŸÍ%¬W¯rCmƒUzÈ2e¯Û¶-¶gž{Ný™ü¶po¿3…¼Ü\¾øüs6mªø)ìª*ï»°lùŠ]Ouì×û/;;›•·zútï^ñÅÇ… X¼h¡Æ¨ôô«ŸE÷Çz°fõ*õy€k×®±xÑBU™=t.?==5«o?•[RRÂ/?ÿ„R©¤C‡Žƒ÷ê݇úffìÞµK#¸ª‹±û÷ciiIë6m*ܦªjÔ¨ïð!-[¶äçŸT\U ¼;{ÞÅÄİoo„ÞQ333Ö¯_ODD¸Æ[\l¬úG¿¾¾>]ºvàç 4R-ÄÇű}û6 E¹•û÷ïSߘU jù懲‡nÝ»k”¥«Z°¶nÌ›o¾…R©ä³O?Ñê©û ö½êªê>ÔŽúffDŸ>­‘+/7Wã)ïsssôôôÈÌÊÒØÇ òóÕc-ß©[7Õ1öÏ?7j¤K¼xáB•Ó]צó9¬:Ÿ«¨¾º8ÖýòóOxþ¹ñRSSiÒ¤ ÞÞš7v«sÞ«êñÑÇ×7wwÒÒÒøþ»ù¿·S/]bõª•( ï·µµ5>¾¾äç³hÑí J¥’ß~ýU+ ×ý¨Î>QÝÏõÒ¥‹êóñùóª÷éÿíÝwxTUúÀñï$„Ò ¤÷$t”®HUQ°¯î®XwíÁîZW±¬®þt)¢tÞ¤’@$@„THÏd~LfÈ03If&õý<ÏCæÞ3÷Ì=÷ž[ÞS4#òœ=sPÿöÆ,¹71§®ûyáBmp `ËæÍ\¾|77wûTKë:–<‡µçydÎþžØ0”ã²¥Kt‚k555ü´p!Õ0’ÖåË—IMM¥‹£# ‰‰:ËzôÀß?€Ó§OsúÔ)eiii¤§§éõ˜¾xñ"Û~Ý @TtT›æÝKžÉU*ÿýækmY¨T*V®X¨·´•J¥ÝŸšs<§áW*•œÏÏÇÅÅ…N¤ÿ1ßþ÷ç–Ë—sñ 5šÎÅÅ…O?ý·N@¿¾¾žm¿þª½&††…У%%%Ì›;Wç_½úrsrpvv&©áÝœÆÈQ£éÒ¥ ‡ÒÓYºd‰N:•JÅÎß~ãh£ºÉÜgesžÝþ¬÷urµ‘ÜœôèH7_?Â""9}*—ze=¾~Ý  ¦¦šÎmùŒX÷»ôþº]œ+**ø×{ïOÝRë×­åïÎà†n`íš5m² sÏV‰1fìØ†y–6›¦¨¨ÂK†[ÊvÌÐH•••œÉË3¸ìLÃC@c›7mb÷®]ôëߟ¸¸x"£¢pwwçÆ¡CEfF&›6mÔkháá¡>N MŽ}­.]º4;DîõbÜmã¹sêTƒóeitêÔéw3ôsvVK—,aü„ <ñäS¨T*Ξ=KvV;vl7ør¢5<üÈ#óëÖ-ïÑÚóÜ3—©ç„æ¿¶§¨[Ï–––êõJ¸ÞÕÕÕñùœÏxôÑÜ~ÇÜ~ÇròÄ öíÛ˯[·ê]W¯î׈šOÏ?ó5ÌœrµDß¾ýf Ë¿?þȤù…:¢®3vžiÎÉÆ÷œ`ÞuÏÔúѳáÞvܸÛ7î6£Û¹¶§¥:߆çr/2PǘËÔsÂ’rmÜàNóÏ”J¬¯éÙbɽ‰9uݵ½·íw[5‰ÅÒºN£¹ç°ö>LÝß®®®X[[S]Uepßu´½ûöR]UERr2;wÖö0p*•Êà4^ÞÞ<õÔÓ„†…ýÞŽèeé3ySÇvkvÖÜ9®Tªÿ/=Ø 3~ S×[M=+_Û¸ÄMzcŠŠÔ©këG÷†{ø ͼ#Ñ0÷YÙœg·ö¾¯»^ÈÔ†òNçRx©?»8¢T*¹xñçóÏ1èu×õòòëë¥þÉçŸÏ!7GmïÒ¥ Oÿóâãã™1ã1žzò º¥óq)›X¯¦¦†_·neÌØ±DDFZ–ùV¶jå ŽÉ$ïôéæWþÈÍÉá½wßá£Oþ¯¯/q±qF{çtO// åLSUVT°aýzjjjyìñÇIJNÖ °%7tA¯««ã©èööÒÜ|õIJ6`»V}}=kV¯¦_¿þDÇÄ0xÈë²5]k3oi0¿´5^þ+Æ3BÓù0”ÏU«Vj[´ÒÔ\¦ª­­åý½Ghh“§LáɧÿÁK/¬ž`>:š¨è’’“IJNfÈ 70kæLƒ×°ßán¸êOp 3·\ÍáíãCoÃZÿÞztZ¢=ëºæÊMÕèì4÷ºgný¸wÏrssæíòe÷ãíT™˜sN´U¹6®BÚôÞÄH]ÕÒw5×u ZúÖçÑñ^°ºªŠ½ûö2xðz÷îî]; ÁÇLJ£Gèõè²²²âÅ—^Ö·ºrÅ òóóµÁªñ&0mú=Í–‡iâÀSµðؾž(Ú`nÐ뙪™J_ÑÄ×’Í×[êŒ]KM½52çYÙœg·ö¼¯»^H€­UT”“}LwšNlpqq¥®®Ž+—/wPÎþ\ÊËËùä£ùô³9øúù1즛t&ê­¬¬Ôö(¹–æ3•JEM3½nÖ¯[˘±c9jTëþ •””´éÄì¿GJ¥’œ“'ñõõ¥«sÇ '`ŒB¡ ,,€ì,ÃÝÍ[Kv¶ºGÛµ-oú4 igggð%@Ÿ¤$¾ûö¿-ÞÖÙ³g‰Ž‰¡{÷îfæö÷ÅÍÝMï3ÍåW®XÔjG©TR~å ]qssãl£ye@=4ˆCÃ|†ÝÜÜ ~¯&¥ZÓ]ºt vlÛÆñã­DkÊš5«IÙ¿ŸÔƒ‰'4,Œ©wÝ­ÇÞ777 t>sqQ^ZÚü Ȧ”””j·aˆkÃçÆ¶ãæ®¿¿]\\P(ÔÔÔPY©ž§¨°•J…••?ÿ´°Mo:KJÕelì7¹5ó›Ú‹NÏ[{{¦M¿‡‘£F1õîi:/Ë Æw/*nÙäØ¿šyDþóå—zcÕ»ºº¶É •özÌ9þ<«WÿÂêÕ¿hç¹yä¯cÒäɬZµ²Õz•‡……qÿråÊþõî»Fƒ«íyîiêþN6úaMõTÒhé9¡iíæ¦?¢•••^¯‘ö¦ÝZL»¸6½._¾Ì¶_eÛ¯¿êaoþùìsÄÄÄÒ»OamKJŠqvvÆÍÕóùù:ßãj¤þër 3­\-±bù2V,_Ö*ßõ{Óžu†¡ã ßsZzÝkiý¨™æØ±£,_Ö²c¡¤XOWu*€›kë×'-='Ú³\--£ë¹®k©ë}S__­...×e/¶Û¶1xðÄ®];8h0Û·é÷^ ¡{÷îäææòï?ÖÛßÞÞ>í’gC,}&¿^]½WµÑë‰Ú’{Õ?ãïMÔû¡´Ô²óK{¿nàÝ‘z;†ë­ÂK—pwwǧ›O‹:[ú¬lγ[{Ý×]/þ\¡çëDPHVVVäŸ;ÛâÖ8Âreee,]º€‰“&ëÓ ZÉhºÍ6¦BâÒ¥KÍÞ<9s†ÌÌ úõëS×®­•ua¦æºÕûúù–_[[rß¾¸ººRYQÁþý–]t E“-âýüüÝá ºtéBd¤z’à¿=ò0“'NÐùw÷Ô;©­­Å»aÉ–òðTwc× CБ|}}騳½-ÄÆÆê}¨ço°”æ;4óH4B¡àìÙ³zCQDEGëµ¶rssÃ××—ÚÚZN5šX:=MÝí¿o¿þfåS¡PÐ30žëXCênê•J%Ÿ|ü•••Ü6~]ýý ®ë¢¢£õÒŵ`¾$Íœ®m]7È5L_Så*ô•µ þnÏû wwwºuë¦÷¹öž¸QcÂÖ¼î5U?jïmû¶ü;5ÇzHH¨^PÃÖÎŽ°&†®k-ÆÎ‰ö,WKËèz®ëZêzßßÕÕÕÚ¹fo>Âämj®ÿömxÝKMMårY½ûôÁÁÁ¢T*Ù¹ó7½u5 /œ?¯÷>ÎÆÆ†Ä^½šÜV[ßã[òL~½ºz¦ûìîáቻ‘FµäzÔØõø¦áåå…×5×…B¡½¿¶´>Ñ¤ŽŽ1øþ2>Áp½¥™»´¥ç¸¥ÏÊ™ûìöG¿¯“[òôòÖyxU(E}}=Y×LÖ*ÚÞšÕ«)))ÁÓÓ“‡Ó~¾¯!r>~ÂxöL½ëî†ušîF«±~Ý:lllˆŠŠjÅœ sÜu÷4úËÃø6‘4lll¸sê]QUUepòöŽ`eeÅ Áƒ™1ã1ÌŸoñ\e|øÑÇ õ4ïð!¯ÎœÙfÛ4x^–:uíÊä)Sزy³Åß¿iãF&NžŒ—×Õ”NNNÜ=m›7m4˜ÖÃà 'jÿ¶²²âÞûîG¡P°cûvãníš5TTT0î¶Û>b„Þ aWWW&Lœˆ««á½¶¶¶¼ÿÁ‡¼ÿÁ‡Ú|™âüùó|ûßoP(<öÄt5ÒxaÊ”):õwHh(ƒ¡®®ÎàXþ¦P*•üºu <ø ÎAÁÁ 1•JÅæÍ› ¦0` QQW_î:::rÇwhç†ÔX²xŸñ¨N€€¦Ü~»E¿`û¶mÔÖÖ’Ü·/½z]í¥jmmÍ>„µµ50:×H[vÓM{»¬náz¬]³;;;ž}îysÔ÷`qqñÜd`ÞPÏ›P__—··Ñ–Ší-ÿœúeÊÐF÷+=zôäŽ;§¶É6˯\¡¸¸ww÷6yY˜Hll¬^=„¯¯/uuuMÂZÊÚÚš§ÿùOÜÜÜY0^‹ævksoöëoðáÇŸðäSO]§´´”ÂÂBœœœ¸áƵŸ{xxrûíwMgê9±oß^ʯ\!66–¤ädíçvvvÜsϽMþ0ÿœ¸eÜ8mïái¼QÅÉ'èÕ»·¶±@LL,Cn¸Á`___†ÜpƒÞ<4]IHLô÷Ö-êëíØ±cññ¹ÚÊÝ×ϱcoiö÷\Tßµõ}ýŸùfN¹ }§¦è×@“ëµ×}Fc÷Ýÿ€NÀ}ÔèÑøùùqþüyí3˜Ý3µ~LOOçxv6aááüåáGô^êÚÚÙqà 7ê4–+,,$==;;;¦Þu—ös…BÁôé÷`ÛŠ/†Í9'Ú«\-½7¹žë:S\ïû{É’ÅL˜0AXÔ°¶¶fܸیÎé¦yþˆlrø;K¨ƒi;±±±áÞûîÃÝÝ´4uÐíZùùêy¬bbbt>ÖÖÖÜwÿÍÎ'ÛÖ÷ø–<“_¯4÷h·Þz«öèܹ3>ôP‹Ž‰–^4®Çç°Æî¿ÿàט±cñööVÏEfa€-++‹¼¼Ó¸¸¸pçÔ»t–9’ÀÀ .—•é5¼×¼£‰eü„‰zåÒ«Wo9 Í}V6çÙíÏz_'CD¶¡°pÜÝ=¨¬¨ ¶¶–.Ž]°±éL}}=öï•ù×:@uu5Ë–,á¾`â¤Ilݲ¥Rɺµkè׿‘|ñÕ8ž²^IpP0öœ;wŽŸú©EÛØ½ke–} lŽ<ó îêÍd– ¼ýλ€ºuí«/¿ÜjÛ»yøÍ$6T|×RÖ+yùÅ[m[¦ ÒþnC¾üâ NÊÔÃ=šQ£GsñÂ.\¸€Mgºw÷ÇÑÑ‘úúz¾úò m ŽŸÀÌY³±··ÇÏÏ{{{–-]Êš5«õÖŸ8iI ó£9té@×®Î:ûäó9sÈË»:ß^wž|êijjj8}ê帻¹á×½; …‚'޳¬¡w'\òÀÁFó}` ‰ôIJfÉâÅ:ËFŒEï>}uxû\½QÚ²yiii¿óÑÇ£ºªÊà²ââbÞ{÷½Ï›:Ö¯_×*,såœ<ɬ×ß ==ªÊJbãâptt$eÿ~vþ¦ß:ÏT»vîä·;8h}ü é‡ÒQÖ)‰¥‹£#™™¬Z¹Ò`ÚãÙÙL½ën’ûöã|~>AÁÁøúúréRóæþOgÝ¢¢">xÿ_<óìs<ò׿1aâ$NŸ:EMM ¾¾¾ôèÙ…BÁ®]»,þMÆlÙ¼™ÄÄ^ 8G{œ·ß|Co²²2>ùô3ÒÓÓ°µµ%..žN:ñÃ÷ß ›jþüùDÇÄËœ/¾$33‡.ÄÅÅammÍÒ%KŒ¶`;qâ3gÍâСt*+*‰‰ÅÉɉƒòëÖ­:릤¤0Þ<¦Þu³ßxƒS§r9wîÖVÖôìÙ/ooŠŠ [|M2æÒ¥K|÷íyø‘¿òÂK/‘™‘Aqq1¡aax{{S\\Ì7_mÑ6,1xðþþè òóóÉÍÍ¡¶¦–îÝ»Lmm-óæÎÕK3Þ<üüºÓ»O>ùô3rNž¤  ‡.] ÁÉɉíÛ¶i„«®®fÏžÝôï?€wÞ{”ýûµ÷iëÖ®m•cÈTË–-%)9™[Ç#6.ŽÓ§NáìâBLL »vî¤WïÞÚag4zôè©Ó¢9²aNØè˜nçNíç{÷îÑÎS{­Å‹~æ¡¿<Ì+3_#-5•ª†zyÎgŸZ< RHhwN½‹ÒÒRŽ?ΕËe¸º¹i[mΟ7—ŠkZøŽ;GÇ«--5¸·ßqõ÷T”—³jÕÕú.)9™¨¨hª«ªpqqåþÔËË‘#™ìnToµ×¹ê_Ó¦OçÑ1rä(jjj aû¶møè馟UUUü端xòé§yö¹ç9|ø0ee¥DGÇPSSCIII“Ãü´õ9qþüyQÑÑüû³9dgeáååEXx8µµµz×0sÍùôSfΚ̈́‰éÝ»7§NŸÂÇۇа0”J%sæ|¦×𾤤DûŽfÚôéŒ9R;òLž=ñõõåí·ÞÔIcγ²9ÏnÖû: °µ¡¼S¹X)8:uÅÞÁêêjÎçŸâxö1ƒ-3DûX·n-ãÆÇÛÛ›!7ÜÀ–Í›©­­eÖÌ™Œ½å LpH …‚‹/²gÍj–.]Úâ.ÝuuulÞ´‘ñ&6¿r é´ºuoÍ0”׎‹l)77w£-Gê|¿ IDAT:zXS{{{–zËìµÿ_¼ègòòNÓ»Oüüº†•µ5ÅEE¤ìßǪ•«ÈÉ9ÙÙ6ÊÕÕU=de%………ÉÌdýºuFóåÝp¡m¬S§N:ŸÙÛ_mAYQQÁ+/¿DRr2ÑÑÑxxzèä¤Úb×ά]»†ºº:@ý0¯éi–z@2p””ý<ðÐC„††êíîë닯¯zøÍºº:ÊÊJÙ¿o[¶lfÏîÝÆ¾’€ãÃM{àjêXHIéØyçÍ›KXX7Ý<777.]ºÄ/«V²léÒV«ÿ“?"33“a7 #..^=üp~>Ë—/cåŠFçyKKKcñ¢EL¹ývúöëGee%[·laþ¼¹ÇéOKMåé'ŸàÖq·‘˜H|Buuu^ºÄÖ-›Ù»w¯v¨Ý¶òÕ—_NïÞ½{Ë-z“Š¿ñúlî½ï>ú$%cggÇéS§X±b¹Å­a5ʯ\ᥞgÂĉôíן¾}ûQ[[KvVkÖ¬æ·;Œ¦]øã>b$îîî²vÍ–,^dðXX²x™‡3ö""#INö§¢¼œ‚‚víÚŞݭÌ\¿n.\`ܸÛ %"2’¢¢"Ö­]ËâE?STTÔ*Û1ÇÒ%KÈÏÏ'<"œ¸Ø8l:wæÒ¥KlÞ´‰eË–lõVWWÇ;o¿¥~ :ŒÀ  ‚CB(--áôéÓ<¢–Â/æÌ¡´¤”>I}vÓMÚ—›û÷ïïÛÉ'xîÙg˜z×]„††5´öÏgþ¼¹¬\±‚ÿûázizôìÁíwè÷‚ŠŽŽ&ºÑyØÖ­]KçζŒ5оýúi[,~ñù‹æŽõKˆÎm‰ŽŽ&88˜.]ºPZZBjj*«V®àPzº^š1coÑ»t~gAAÎKgMÙÙÚÙ1fìXƒy±¶¶Ö °eçž½ƒƒvÈ•t¿£±åË–Ò©S'n>œÀ  .^¼È°níZ†0<Ô‹9çÄo¿íàJù&OžBXÃCzZZ*ß÷¯Í~½ÉII¨T*ÒRSùñÇÚ¡l5̹îyõcAAÏþóŒ3†~ýúJ§N(**"-5•ýû÷é]#Î=Ë Ï=Ë]wO#..ŽnÝ|9yò³^›É7mµ›¹çD{”«¹e¤q=×u¦ºÞ÷÷µùëѳ'eeeäæä°iÓF£#åhÞÝ=m:‘‘‘Œ3…B¡`s°·7xÏééé©óùÖ-[ ØŽ=JAAžžžTWW³w¯ñ£>þðC²oÉæÆ¡CIHL¤ºªŠÌ#™,\°@=…@6hùýLŸ¤$ƒ£]\ÀÛºe³NðÆÜgòëUÖ±c¼ÿ¯÷¸ãŽ;ñõóÃÁÁ=»w1wî\^}µù€L¹i´u™«¨¨ˆ÷ß{{î»z ;xð ?Οߢ¹ÏZâøñã<÷ì3Lž<…ظ8 HEy9{öìféâÅ72eCãw4ñ ôIJ¢¦¦†üü|þï»ïÈÌÈÐYßœgesžÝþ¬÷uŠÈø?Þ¯B!:È˯¾JBB"3_}…ŒÃ‡›O ,²`áOØØØ0yℎΊâO¬ORÏ¿ð"åW®ð÷¿ýµUêÅŸ\ÄR× !~OÂÂÂxëwÉÈÈ`æ+­7Z˜øã’9Ø„B!„±±q€zø ® !„B!ÄŸƒØ„B!„±qq”””°ú—_::+B!„B!Ú‰ÌÁ&„B!„xúÉ':: B!„B!Ú™ÌÁ&„0Ë£3#$4Ĥ4óçÍc_“×þ}ôÉ'&§y}Ö,ŠŠŠÚ 7Bˆ?“‘£F3jô(“Òìݳ—óç5¿¢Ð#û[M®{B!„B!„šØ„B!„B!„B!„0ÌÁ&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ $À&„B!„B!„B!„ :ut~/œºvÅËË7ww\Ýܰ³³`ÝšUTWUMçàÐ…ˆ¨h<½¼°±éLEy9y§s9ž…J¥j¯ìÿi,Z²Ô¤õÿþ×G¸xñ¢öï2fÌXzôè••gÏžeãÆ lX¿Þhy3iòd¢"£°³·§ðÒ%vïÞÅ¢Ÿ¦ÊÀ±1oþlíìxî™g8qâx‹óУ·OLL,...”——s© €Ã‡Ù»{7G5é·7ìٯ·xý‚‚þöÈÃ<õ0pà &×ÏÎÊâ…çŸÓùÌÚÚš‘£F1xðü°²²¢¨¨ˆ¼¼ÓÜÊ9æhrú>ûü |||xèû)))éèì´™ž¼ÿÁ‡=z„—_|±£³#„B!„BˆkH€­…BBÃñèaRGG'ß8 ʯ\¡¬´W7w"£cqs÷`ïîdkeGÑû,""€“'ORSS­³¬¦¦Fûÿ;§Neò”ÛQ©TäääPWWGHH?òWÂÃÃùôßÿÖûî„„D^xé%¬­­9þ­—/KÊhä¨Qz6!®Gß}ÿNNNL»kªÁÀ~kùç3ϪÏé·Þ$eÿþ6ÛŽh_R®B!„B!DÛ’[ •–”PY©îaRVZˆѷ4›&¡WolllÈÍ9Izêììì4d(Þ>ÝèÑ“S¹9mõ?C-¼5½Ÿ>ùèCΞ=k0]PP“§ÜŽR©äÙ³8tèÌ~ãMn¸q({÷ìeÏžÝÚ4¶¶¶Ìxüq¬­­Y0‹- K—.¼òêLBBC™:õ.¾ýoÓ½sšÓµkWñ:uâ¿ß|ͺµkµ=‡¬¬¬HìÕ‹øø‹¶‘“sRoßizµ]¾|¹E-ç7oÚÄO lÑö¦L™BDd$'Oœàý÷ÿÅÅ ´Ë<<<1rÕÕWƒdæ–«9Ú«\5ÒRSùìSuðÖÊÊŠƒñð#eâ¤É±vÍíº)ûSèÕ«7qñ Ú|5æåíõõõ¤¦¥ê-7¥Œ4¾úêKrs®ÖS7Î_ÿöw¹yøÖ­½š?SË ¬¬L§|ß~ç]BÃÂXºt)›6n0˜'KÊH¥RѳgOÂÂÂÈÊÊÒ[þ{¶~Ý:Rä|~~GgE!„B!„Bˆ?<™ƒ­…NžÈæhf/œ§¦¶¶Ùõ]\ps÷ ¶¶–ŒCW‡…ªªªâè‘ ƒCÚ,¿Â4£ÇŒÔMp Ô=g–-]À˜[tƒªý ÀÅÅ…sgϲdñbíçååå|óÍ×€:akkkQÞú$%aggGJJ kV¯Ö–¯¾¾ž”ýû[-ØÓ^ ¨‡êk„u/«ùóæ’™™ÑYk·r5¤¾¾žíÛ¶ñùgŸp×ÝÓèܹ³vùu„ððplíìôÒÇ7ô^;vô(åW®´zþ6nØ  4÷í×WgY{•«%etèP: îÅöG³wÏV­ZIaaaGgE!„B!„Bˆ?<éÁÖF¼¼}(¸xAg5€óùçP©Ttíꌽ½=•••‘EÑHb¯^¤¤è¡´ÿ~¦M¿‡ÈÈHòJLÔ¤IÑêóxv6eeetíÚ•èèЮ¯¥ÜÜÜ(°pN±ë‰æ7],¸þ~S{•kSvïÞEqq1®®®$%%óÛo;õÜwgòòèîïOtT´ÞöµÃC¶Q¾4²³²éÛ·®®®:Ÿ·W¹ZRFy§ó°édÀƒøîÛo¹bB ÒÇLJÎ;S§Tr®{M6¦°Rpë¸qŒ9 OO Ù¾íW/ZD­Æo¼õ–v¸Tææ`S( 1‚a7ÝL÷îݱ²²"?ÿÛ·mç—U+ n "2’ÛnO`P ÎÎêy /^¸ÀÁƒX¾l™ÎЬ|øñ'dgeñƯs÷ÝÓHJNÆÑÑ‘üsçX°`>ûöî5¸nݺ1yÊbãâpvváÊ•+=r„Å‹~æäÉ“­’fÁŸ°±±áÞ{¦sÏ=÷œÜ[;;NŸ>ÍÊå˵ç\kii¹›“pîü:ÿ¸`>‹~þÙ¢>žIS¦LRÉ¡ôtþï»ïšýmÿùæÜÜܹÿ¾{ñóócÊíw‚••çÎåçŸ~bÿ¾}Úõ?a"ýú÷ÇÓÓ“ÚÚZrssX³z5»vîÔûþ 'r÷´éü´p!¹¹9Lžr;þþþ”——“²óçÍ£´´´Ù|šÂ×Ï{ィ¨(P(ÈÊÊbá‚ùz=q--W!„B!„B´œØÚH׮Δ5Ì!ÔX]]åtéâˆSWg °u0'''\\\8“§?Õ¹³gQ©TXYYáççÇñãÇðP§9cx¼<¢££ñð·(àq±!°–Ø«×& {ñâE|||0` Îƒ×ƒö*צ¨T*²²ŽÑ·o?BÃBu^>8Bwââu¶¯P(ˆÔ§¶dc£¾téÎÅ×^åji­_·ŽÈ¨(†ÆÊ+Z¼Ý§ÿñO‚‚ƒ)**äá‡2/ó͸ãŽ; %##ƒÓ§OÃä)·Î³gé7oÚDzšºWÞnÐ{±nÈãO<Éà!C¨©©!ãðaê”uDGÇ0mútx}Ö,½†!ý àéü•JÅ‘#GÈÌȤ«sWzpÇSÙ¼i“Ážs666Ìš5W77rsrðôò" Gžyö9^~ñ½à@Pp0³fÍÆÞÁ3gÎpìè1¼}|èÛ¯½ûôá½wÞÑ+SsÒh¼ôÒËøûûsøða:ÛÚÃSÿøÎ.άþå—f÷eKµ´\+*+ùiáBmºñ&йsg–,^L]]öóÌŒL‹ó´ú—U8::0`à@ºwïÎŽíÛ9wîœv ×ôDmœ¯»§MG¡P››KÖ±cØÙÙNb¯^FçŒ3e8ˆ'Ÿ~€£GŽPVVFdT³ßxC§goS˜ñØãÔÕÕ‘ŸŸ££#AAÁDEGkl¼ùöÛøûPZZÊ”ˆŠŽ&**šE?ÿÌ æþþÄD¦Ü~;999ìß¿àànºy8ÑÑ1<ÿܳ&ð›âìì›o¾EuM ©i©xzzOtt4o¾ñ:‡ÒÓµëZR®B!„B!„0ØÚˆ½½=€öSXxž^ÞdN§¤¸˜ªª*ºtqÄÞÞ¡#³)P÷Ð()шÖ××SVV†³³3îÚ›&¡4 ž·ÀÝÃâü¥ìßOqq1ÞÞÞüû³9lÙ¼™´´TŽggS]]mÑww”60mútþòðÃôêÕ‹Ý»w“™qøºxé×^åÚMEïk&)))Œ»m¼v8Hàà`¹té§OjÓ¼Å4òvïÒíÙÑ^åjiíÚµ“û|á#F²jåJ½ UG  áõY¯i‡ªuvvæÍ·Þ&>>ž¡Ã†±yÓ&õÿÖl€­_ÿþ 2„’’^~ñΟ?¨çz|ýÍ·ˆ‰‰eìØ[X±b¹Nº‰“&¡P(xÿ_ï±g÷Õ¹( ½z÷¦ÂHà¿g` )û÷óÒ‹/P]]B¡àÑqãСŒ3V/À6cÆcØ;8°bù2þ÷ÃÚ²3f,<ô>ö{äaÞræ¤ÑpusãñÇfP\¬GDDðÚì×™~ϽìÙ½»Õ†Ûli¹VVTèÌ“8z̘†Û"ƒ+K4hн{w¶oßFÊ~ýžÜ%$$2mú=TUUñþ{zP»¬sçÎŒŸ0Áè9ÕÒýmooÏÃ<‚B¡àƒ÷ÿ¥íEfïàÀk³fÜÂ!¶ï½ï~V®XÁO Ô–¿‡‡'þþÚu¦Þu7þþdddðÖ›oPݰŸCÃÂxmÖl&MžÌ¾½{9qâ¸Þ÷‡……1Þ<–,VÏimmÍ?þù É}ûrÇSùoõ–êÖ­à½wßÑöx¼åÖqÜwÿýüíï2ãïÓmn¹ !„B!„Ât2[±î¤Ž]*•JlllˆˆŠÁÝÓаõçuêÞ:YwX…Z㹬 ½€mü¹½ö3»†tÍ¥±70W–)ÊËËyïÝw¸pá®®®Lœ4‰Y³_çûÿÍå¥W^%11Ñ¢ïo-·ßq‹–,5øoÂĉ:ë®X¾Œ5«W£R©èݧΘÁœ/¾ä‹¯þÃ]wOÃÑѱƒ~Eû•ks4/Óºè|~ôÈ***ð÷ÐÉŸÀÁ&zÕ™RFײ²²Â§[7úËÃDGG³iãF6mܨ³N{•«¥eTWWÇ–Í›ñõõ%&6¶UòÔZvîüMgÈÒÒRþ¨¸ »é&‹¿ÿ¦›‡°dñ"mp  ¬¬Œ¹ÿûA½Îðázé\]ÕÇZö51•JEÊþýTVTÜžJ¥â¿ß|­m  R©´½ƒCtƒ$¡aaôèAII óæÎÕ Ò¬^ý ¹998;;“””lQšÆ~Z¸Pì8zô(¿íØŽ ƒ‡ 1˜Æm]®íiâäI,[ºD'¸êsï§… 6þhéþNNîKGG2Ö¢±²¢‚¾ÿ¾ÅyÍËËcîÿ~Щ+.]*àà€: vãС|ûÍ7Úà¨õ Ö£P(¸éæ› ~ÿÅ‹Yºäêž­[¶¯°H9`ô;M)#÷?øPç þõÞ»:½˜4Ú½\-(£ ë×1î¶Û1r¤ÎðjMyö™šœESn„Ñ8tH¿àà …E=îÂBCHKMÕ[–ž–†J¥ÂÏÏ{ YNN‰‰‰<ðàCüüÓOœ>}ªEù(,,Ôs«QÐ0GŸ“““Îç¡ yËÈ8¬7D%@jj*= ½:lª9i;|H¿ì:Ä 7%$$´Ùß×Rm]®íÅÖÖ–ðpuC¡6˜œ¾¥û;¤¡\ÖßoG23©­­ÅÆÆ¦Ùí­þeU“Ë»w÷ÇÞÞž’’NÊÕ[ž–šÊ-·ÜªÍϵ2Ö+·‹.pñâE¼¼¼ðóóãôéÓÍæ³9………:C<‚ºž;|è¾¾¾„„†’žžfñv„B!„Ba °µeÃ\)šÖË{wÿ¦³Üªáóº:ý‚¢}U5jißÙÖV§»†­­múW—UWWãàà`t.Ci,¡T*Ù·o/ûöíÀÃÃQ£ÇpÛøñÜrË­LI!-­ã^°mܰQgh³–¸xñ"Ë—-cù²eX[[Ͻ÷ÝO÷îÝùû£3xéÅÚ(·Æµw¹co¯î}UQQ®·ì@Êú÷@||[·lÁÖÎŽððpjkkI7ð[Ü2:yâ•UUØvîLwxü‰'™U<“¬cÇ ¦iërm2:þ<éii$'÷ÅÕÕÕ¢ü´¦¢"ý@gII *• (/×?&ZÂÚÚš. ½ m§ººšŠòrº8:âìì¬`ûþ»oéæó"ýú÷§_ÿþ”——““s’ô´46¬_ÏåË— nÓÐ0žš^8×öîqvVÏ…i,Ø[T¤kæÌ47îrýtšVÆÒ˜£-˵=¹ººbmmMuU•Ñ!Z›ÒÒýíì쬳¬±úúzJKKñhÁ0½ù×¥®¥ÙŽ¡|ÁÕãÊÅÅÙàòâbÃéJŠ‹ñòòÂÙÅZ!ÀVl$šík~‡B!„B!Ú— ÙF*棱32D™­]Ãz†‡Õí§¨Ñ;.^R) mO‹ÆóñhÒ9y «}q×Jsø\ëÒ¥KÌýßlݲ€¤ä¾m²ö¢T*9xào¾>•JExDN]»¶{>:º\5<½¼¸Ðh?ƒRP©TÄÆÅ¡P(ˆŽŠ¦S§Ndfd [âóÏç0ó•—yþ¹gyä/‘––†­­-3f<Ö¢áÏÚ¢\[«ŒÖ¯[‡µµµÑáß:D3½˜Ú¼—“¦{õ5Û9sæ O=ùo½ñ+W¬ ÿÜ9¢¢¢¹ëîi|ðÑǸ¹¹ø2P5Ì eʦ/W4dMÕè3ÓÓ´:â7]åªP(  »¡|+))!çäI\\\èA\B<))ûÛ,O žð“>¤üÊ|ýüLš;ª5˵µÊhÿþ}róðXY]—C7wý@•‹‹ …‚ššmã s(•Jʯ\Qo§Ñü}¶¶¶888†µµµ8Â÷ÿ÷Ï?÷,þí¯=z777n7Îì|ihzD¹¹ëç ®Î§9ŽÌMÓ˜¡ýàââÚds´e¹¶§ââbêëë±µ³3«‡_K÷·¶\Ýô{—ZYYµÚµ¡¤¤Ôh¾\ÝL?~àjo¼Ö:† ?Ðèø6£7¡B!„B!,w}¼Qüºxá^Þ>z½<¼}º¡P(¸\V&=Ø®iiêùˆz÷,±W/Ž=ª3dš&M¯^½õÒáììLmm-‡32,Ê[s½„üºû†‡b»^5õ›4ÃÓAë¾àn©ö*צ$÷U[XYQÁþýû ®“’’@||<ññêÛÆç_k-eee,]º€‰“&ë”e{•kk•‘R©dÓÆMxxxü®kùúúÒ30Ðh`¯5ÄÄÆê}ÀñãÙ÷tÊ>®ØÆ7ÌÙ×X\\< …‚³gÏêÔuưjåJ¼½½-ÊÀñluÞ¢£c Kñ dÍzæ¦i,&6Nÿ³˜è&Ó€:°Ò30žtêÔühÛæ–«f8Mc½á[Km]˶S]]ͱ£G¸yø“·ÓÒý•@TtŒÞú‘‘-š­%ΜɣªªJÝX¡G½åšºÕر­í%©áé鉗·7UUUœi˜w÷ZöÚãDZaØÖ¦¸»»Ó­[7ýí7C†b@ËËU!„B!„æ‘[)))¦¨¨¢b®¾P²µµ#2JýBää‰ã•=qµkÖ0즛ˆŠŠÖ~îëçÇĉ“XýË/:i~Û±ƒË—/Ó½{wÆO˜¨ýÜÞÞžú ›6n& X| ~IDAT´xȾ^½{óâË/eàE^ß¾ý5j4ûöîµh;íéåW^eü„ Ú¡75œyâÉ'Q(=z¤Czx¶W¹beeÅ Áƒ™1ã1ÌŸOu£9;p@`rãøûpîÜ9Îçç·zž Y³z5%%%xzzrãÐaÚÏÛ«\[³Œ6nXO}}=q /Ò›òäSOóþòêÌ™æg¾ Ô©ƒ¹ãŽ;ؼi“Åß¿iãF&NžŒWÃ0¤NNNÜ=mZÃv6ꥻmüx½^CVVV 0€sçÎê¥1UVVyy§qqqáΩwé,1r$A\.+Ó :›“¦±)S¦èôÄ eÐà!ÔÕÕ±cûv£y½eÜ8ÞÿàCÞÿàC<<=›ýmæ–륂"£¢šÝ†% .ª·Õ‚í,Y²€ &hPÖÖÖŒw›Ñù[º¿÷íÛKù•+ÄÆÆ’”œ¬ýÜÎÎŽ{î¹·å?¬J¥’_·nàÔÉwPp0ÃGŒD¥R±y³á2òòöfÜm·iÿ¶¶¶æž{ï`û¶_Q* ϳ¥=~úöëߢ¼Þwÿ:ÁÜQ£GãççÇùóçÉÈ8l0)å*„B!„BÓ5ßìZê^gaá‘ê?Å8úõH}½ºåùñìcä7zɘv …A7 %0(O//ʯ”ãæîŽ /œçô©œöü ¢ ÇŽeÅòeŒ»m<³^ãÙÙÔÕÕ޵µ5Û·mc×®:i*++ù|Îg<óìsL›>aÆQXXHPP]9“—ÇüysnóÑÇ3(..æ½wßÔÃöêÕ›^½zsùòeÎäåQ§TÒ­›ê»Ö¯#==­•ö†yn~3‰‰‰—)땼üâ‹Ú¿ºveÚô{¸{ÚtòNŸ¦¸¸'''ü°±±áòåËüçË/Û+ë:,-×1cÇп¿ñ¦ï½ûÅÅÅÚ¿ã˜9k6öööøùùaooÀ²¥KY³fµÑï9žMYY={ö®ÜšbJ5¥ººšeK–pß0qÒ$¶nÙŒR©4«\»víÊ /¾¤ýÛ? €‰“&qsÃÜhDzŽñß~«]ÇÒ2j¬°°””ý$%%7¿r;8qâ3gÍâСt*+*‰‰ÅÉɉƒòëÖ­:ë&%%¤ýÛÇGÝÃeèÐaD7êùó˪•”——°kçN~Û±ƒƒñÑÇŸ~(e’ØØXº8:’™™¡í•ÖØÝÓ¦s÷´éääœäܹsX[YŠ———.]2˜Æs>ý”™³f3aâDz÷îͩӧðñö!4, ¥RÉœ9ŸéÍI£QVVÆ'Ÿ~FzºznÁ¸¸x:uêÄßOACp«5˜R®mÛö+aáá<öø$'÷m˜TEêÁT>ÔjùÛùÛÆO˜ÀÈQ£ñòò&ïLõJ%ùùùÚ ¬ÆÁ˜?oSﺋWf¾ÆÉ'8{î,vvv„……ãììÌúõë n§¥û»ªªŠÿ|õO>ý4Ï>÷<‡¦¬¬”èèjjj())1kˆJCæÏŸOtL,11±ÌùâK233ppèB\\ÖÖÖ,]²Äh¶ãÙÙL¿ç^úõëÏ…  Á§[7.^¸À‚ùó[%çóó çߟ~FVVžDDDRWWÇŸÏ1ÚÒ”rB!„B!„é$ÀÖB;ÛjçâhÌÙåêü ¶¶¶:Ë._.cÛ–MDDEãáé…§W**Ê9ž}ŒÙYõ%Z×ßONN£GÁ? +++N:ŦY¿n­Á4ûöîå¥_`â¤IDDDâåíMaa!7ndÑ¢Ÿ›f- !`Hãà_ï¾KRr2AÁÁø`ooÏåË—IIIaóÆìÙ³ÛüÞJÜÜÜqs3ýúõ#>!///|ýüPÖÕ‘îdåŠå:ä¥%åêáá© |ríÐf®®®êá ++),,äHf&ë×­#'çd“yT©T< 7à`Jó6Sʨ9ëÖ­eÜøñx{{3ä†زy³YåjccChX˜Þ÷{{{k‡,¯(×[nI]kýÚµ×M€má  døˆ‘¸»»STXÈÚ5kX²x‘Þ5£OR75!»qèP¿·nÙ¬ °|òñGdff2ì¦aÄÅÅceeE~~>Ë—/cåŠ{Ý|õåÄÅÅDRR2õõõ\äçŸ~â—U+¹Ò0·›¥Ž?ÎsÏ>ÃäÉSˆ‹cÀ€T”—³gÏn–.^Ìñãú=¿ÍI£ñÆë³¹÷¾ûè“”Œ§ObÅŠåMö^3‡)åÚØºµkélÓ™¡7 £_ÿþÚú£ªªªUl¹¹¹¼ûö[L˜8‰ˆÈHíÐȇÒÓ b–,^DfÆaÆŒ½…ˆÈHzôìIYY¹99lÚ´Ñh@Ó”ýýÛo;¸R~…É“§Ö,MKKåûï¾ãµÙ¯·Úo/¿r…—^xž 'Ò·_úöíGmm-ÙYY¬Y³šßvì0šöÀ,[¶”É“§Ð·_?***ؼióçÍÕÎíÚJJKxç·¹÷Þûè“”„J¥"-5•\ NÓSËU!„B!„¦Q„DÆK”G!„ þ„ “'Nèè¬ü)üÑö÷„‰¹{Út~Z¸ŸþØÑÙB!„B!D‘9Ø„B!„B!„B!„0Ø„B!„B!„B!„0Ø„B!„B!„B!„0ÌÁ&ÄÌGŸ|brš×gÍ¢¨¨¨ rÓ1BBBxô±ÇLJSTXÄë³gµQŽ„B!„B!„Bü‘H€M!„B!„B!„BÈ‘B!„B!„B!„B˜@lB!„B!„B!„B˜@lB!„B!„B!„B˜@lB!„B!„B!„B˜@lB!„B!„B!„B˜@lB!„B!„B!„B˜@lB!„B!„B!„B˜@lB!„B!„B!„B˜@lB!„B!„B!„B˜@lB!„B!„B!„B˜àÿ}zR†i©ŸIEND®B`‚bpftrace-0.23.2/man/000077500000000000000000000000001477746507000141605ustar00rootroot00000000000000bpftrace-0.23.2/man/CMakeLists.txt000066400000000000000000000001431477746507000167160ustar00rootroot00000000000000add_custom_target(man ALL DEPENDS adoc_man man_man) add_subdirectory(adoc) add_subdirectory(man8) bpftrace-0.23.2/man/adoc/000077500000000000000000000000001477746507000150665ustar00rootroot00000000000000bpftrace-0.23.2/man/adoc/CMakeLists.txt000066400000000000000000000016421477746507000176310ustar00rootroot00000000000000find_program(GZIP gzip REQUIRED) find_program(ASCIIDOCTOR asciidoctor) file(GLOB FILES *.adoc) set(GZFILES "") 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 ${GZ_MANPAGE_FILE} COMMAND ${GZIP} -nc ${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) else() message(WARNING "asciidoctor not found, building without bpftrace manpage") add_custom_target(adoc_man) endif() bpftrace-0.23.2/man/adoc/bpftrace.adoc000066400000000000000000003303361477746507000175140ustar00rootroot00000000000000= bpftrace(8) :doctype: manpage :toc: true :toclevels: 1 //// Style guide: - one sentence per line //// == Name bpftrace - a high-level tracing language == Synopsis *bpftrace* [_OPTIONS_] _FILENAME_ + *bpftrace* [_OPTIONS_] -e 'program code' When _FILENAME_ is "_-_", bpftrace will read program code from stdin. A program will continue running until Ctrl-C is hit, or an `exit` function is called. When a program exits, all populated maps are printed (more details below). == Description bpftrace is a high-level tracing language for Linux. bpftrace uses LLVM as a backend to compile scripts to eBPF-bytecode and makes use of libbpf and bcc for interacting with the Linux BPF subsystem, as well as existing Linux tracing capabilities. == Examples Trace processes calling sleep:: ---- # bpftrace -e 'kprobe:do_nanosleep { printf("%d sleeping\n", pid); }' ---- Trace processes calling sleep while spawning `sleep 5` as a child process:: ---- # bpftrace -e 'kprobe:do_nanosleep { printf("%d sleeping\n", pid); }' -c 'sleep 5' ---- List all probes with "sleep" in their name:: ---- # bpftrace -l '*sleep*' ---- List all the probes attached in the program:: ---- # bpftrace -l -e 'kprobe:do_nanosleep { printf("%d sleeping\n", pid); }' ---- == Options === *-B* _MODE_ Set the buffer mode for stdout. Valid values are:: *none* No buffering. Each I/O is written as soon as possible + *line* Data is written on the first newline or when the buffer is full. This is the default mode. + *full* Data is written once the buffer is full. === *-c* _COMMAND_ Run _COMMAND_ as a child process. When the child terminates bpftrace will also terminate, as if 'exit()' had been called. If bpftrace terminates before the child process does the child process will be terminated with a SIGTERM. If used, 'USDT' probes will only be attached to the child process. To avoid a race condition when using 'USDTs', the child is stopped after 'execve' using 'ptrace(2)' and continued when all 'USDT' probes are attached. The child process runs with the same privileges as bpftrace itself (usually root). Unless otherwise specified, bpftrace does not perform any implicit filtering. Therefore, if you are only interested in events in _COMMAND_, you may want to filter based on the child PID. The child PID is available to programs as the 'cpid' builtin. For example, you could add the predicate `/pid == cpid/` to probes with userspace context. === *-d STAGE* Enable debug mode. For more details see the <> section. === *--dry-run* Terminate execution right after attaching all the probes. Useful for testing that the script can be parsed, loaded, and attached, without actually running it. === *-e* _PROGRAM_ Execute _PROGRAM_ instead of reading the program from a file or stdin. === *-f* _FORMAT_ Set the output format. Valid values are:: *json* + *text* The JSON output is compatible with NDJSON and JSON Lines, meaning each line of the streamed output is a single blob of valid JSON. === *-h, --help* Print the help summary. === *-I* _DIR_ Add the directory _DIR_ to the search path for C headers. This option can be used multiple times. For more details see the <> section. === *--include* _FILENAME_ Add _FILENAME_ as an include for the pre-processor. This is equal to adding '#include _FILENAME_' at the top of the program. This option can be used multiple times. For more details see the <> section. === *--info* Print detailed information about features supported by the kernel and the bpftrace build. === *-k* This flag enables runtime warnings for errors from 'probe_read_*' and map lookup BPF helpers. When errors occur bpftrace will log an error containing the source location and the error code: ---- stdin:48-57: WARNING: Failed to probe_read_user_str: Bad address (-14) u:lib.so:"fn(char const*)" { printf("arg0:%s\n", str(arg0));} ~~~~~~~~~ ---- === *-l* [_SEARCH_|_FILENAME_] List all probes that match the _SEARCH_ pattern. If the pattern is omitted all probes will be listed. This pattern supports wildcards in the same way that probes do. E.g. '-l kprobe:*file*' to list all 'kprobes' with 'file' in the name. This can be used with a program, which will list all probes in that program. For more details see the <> section. === *--no-feature* _feature,feature,..._ Disable detected features, valid values are:: *uprobe_multi* to disable uprobe_multi link + *kprobe_multi* to disable kprobe_multi link === *--no-warnings* Suppress all warning messages created by bpftrace. === *-o* _FILENAME_ Write bpftrace tracing output to _FILENAME_ instead of stdout. This doesn't include child process (*-c* option) output. Errors are still written to stderr. === *-p* _PID_ Attach to the process with _PID_. If the process terminates, bpftrace will also terminate. When using USDT probes, uprobes, and uretprobes they will be attached to only this process. For listing uprobes/uretprobes set the target to '*' and the process's address space will be searched for the symbols. === *-q* Keep messages quiet. === *--unsafe* Some calls, like 'system', are marked as unsafe as they can have dangerous side effects ('system("rm -rf")') and are disabled by default. This flag allows their use. === *--usdt-file-activation* Activate usdt semaphores based on file path. === *-V, --version* Print bpftrace version information. === *-v* Enable verbose messages. For more details see the <> section. == The Language The `bpftrace` (`bt`) language is inspired by the D language used by `dtrace` and uses the same program structure. Each script consists of a <>, an optional <>, and one or more <>s. ---- preamble config actionblock1 actionblock2 ---- === Action Block Each action block consists of three parts: ---- probe[,probe] /predicate/ { action } ---- Probes:: A probe specifies the event and event type to attach too. <>. Predicate:: The predicate is an optional condition that must be met for the action to be executed. Action:: Actions are the programs that run when an event fires (and the predicate is met). An action is a semicolon (`;`) separated list of statements and always enclosed by brackets `{}`. A basic script that traces the `open(2)` and `openat(2)` system calls can be written as follows: ---- BEGIN { printf("Tracing open syscalls... Hit Ctrl-C to end.\n"); } tracepoint:syscalls:sys_enter_open, tracepoint:syscalls:sys_enter_openat { printf("%-6d %-16s %s\n", pid, comm, str(args.filename)); } ---- The above script has two action blocks and a total of 3 probes. The first action block uses the special `BEGIN` probe, which fires once during `bpftrace` startup. This probe is used to print a header, indicating that the tracing has started. The second action block uses two probes, one for `open` and one for `openat`, and defines an action that prints the file being `open` ed as well as the `pid` and `comm` of the process that execute the syscall. See the <> section for details on the available probe types. === Arrays bpftrace supports accessing one-dimensional arrays like those found in `C`. Constructing arrays from scratch, like `int a[] = {1,2,3}` in `C`, is not supported. They can only be read into a variable from a pointer. The `[]` operator is used to access elements. ---- struct MyStruct { int y[4]; } kprobe:dummy { $s = (struct MyStruct *) arg0; print($s->y[0]); } ---- === Comments Both single line and multi line comments are supported. ---- // A single line comment interval:s:1 { // can also be used to comment inline /* a multi line comment */ print(/* inline comment block */ 1); } ---- === Conditionals Conditional expressions are supported in the form of if/else statements and the ternary operator. The ternary operator consists of three operands: a condition followed by a `?`, the expression to execute when the condition is true followed by a `:` and the expression to execute if the condition is false. ---- condition ? ifTrue : ifFalse ---- Both the `ifTrue` and `ifFalse` expressions must be of the same type, mixing types is not allowed. The ternary operator can be used as part of an assignment. ---- $a == 1 ? print("true") : print("false"); $b = $a > 0 ? $a : -1; ---- If/else statements, like the one in `C`, are supported. ---- if (condition) { ifblock } else if (condition) { if2block } else { elseblock } ---- === Config Block To improve script portability, you can set bpftrace <> via the config block, which can only be placed at the top of the script before any action blocks (even `BEGIN`). ---- config = { stack_mode=perf; max_map_keys=2 } BEGIN { ... } uprobe:./testprogs/uprobe_test:uprobeFunction1 { ... } ---- The names of the config variables can be in the format of environment variables or their lowercase equivalent without the `BPFTRACE_` prefix. For example, `BPFTRACE_STACK_MODE`, `STACK_MODE`, and `stack_mode` are equivalent. **Note**: Environment variables for the same config take precedence over those set inside a script config block. <> === Data Types The following fundamental types are provided by the language. Note: Integers are by default represented as 64 bit signed but that can be changed by either casting them or, for scratch variables, explicitly specifying the type upon declaration. [cols="~,~"] |=== |*Type* |*Description* |uint8 |Unsigned 8 bit integer |int8 |Signed 8 bit integer |uint16 |Unsigned 16 bit integer |int16 |Signed 16 bit integer |uint32 |Unsigned 32 bit integer |int32 |Signed 32 bit integer |uint64 |Unsigned 64 bit integer |int64 |Signed 64 bit integer |=== ---- BEGIN { $x = 1<<16; printf("%d %d\n", (uint16)$x, $x); } /* * Output: * 0 65536 */ ---- === Filters/Predicates Filters (also known as predicates) can be added after probe names. The probe still fires, but it will skip the action unless the filter is true. ---- kprobe:vfs_read /arg2 < 16/ { printf("small read: %d byte buffer\n", arg2); } kprobe:vfs_read /comm == "bash"/ { printf("read by %s\n", comm); } ---- === Floating-point Floating-point numbers are not supported by BPF and therefore not by bpftrace. === Identifiers Identifiers must match the following regular expression: `[_a-zA-Z][_a-zA-Z0-9]*` === Literals Integer and string literals are supported. Integer literals can be defined in the following formats: - decimal (base 10) - octal (base 8) - hexadecimal (base 16) - scientific (base 10) Octal literals have to be prefixed with a `0` e.g. `0123`. Hexadecimal literals start with either `0x` or `0X` e.g. `0x10`. Scientific literals are written in the `e` format which is a shorthand for `m*10^n` e.g. `$i = 2e3;`. Note that scientific literals are integer only due to the lack of floating point support e.g. `1e-3` is not valid. To improve the readability of big literals an underscore `_` can be used as field separator e.g. 1_000_123_000. Integer suffixes as found in the C language are parsed by bpftrace to ensure compatibility with C headers/definitions but they're not used as size specifiers. `123UL`, `123U` and `123LL` all result in the same integer type with a value of `123`. Character literals are not supported at this time, and the corresponding ASCII code must be used instead: ---- BEGIN { printf("Echo A: %c\n", 65); } ---- String literals can be defined by enclosing the character string in double quotes e.g. `$str = "Hello world";`. Strings support the following escape sequences: [cols="~,~"] |=== | \n |Newline | \t |Tab | \0nn |Octal value nn | \xnn |Hexadecimal value nn |=== === Loops ==== For With Linux 5.13 and later, `for` loops can be used to iterate over elements in a map. ---- for ($kv : @map) { block; } ---- The variable declared in the `for` loop will be initialised on each iteration with a tuple containing a key and a value from the map, i.e. `$kv = (key, value)`. ---- @map[10] = 20; for ($kv : @map) { print($kv.0); // key print($kv.1); // value } ---- When a map has multiple keys, the loop variable will be initialised with nested tuple of the form: `((key1, key2, ...), value)` ---- @map[10,11] = 20; for ($kv : @map) { print($kv.0.0); // key 1 print($kv.0.1); // key 2 print($kv.1); // value } ---- ==== While Since kernel 5.3 BPF supports loops as long as the verifier can prove they're bounded and fit within the instruction limit. In bpftrace, loops are available through the `while` statement. ---- while (condition) { block; } ---- Within a while-loop the following control flow statements can be used: [cols="~,~"] |=== | continue | skip processing of the rest of the block and jump back to the evaluation of the conditional | break | Terminate the loop |=== ---- interval:s:1 { $i = 0; while ($i <= 100) { printf("%d ", $i); if ($i > 5) { break; } $i++ } printf("\n"); } ---- ==== Unroll Loop unrolling is also supported with the `unroll` statement. ---- unroll(n) { block; } ---- The compiler will evaluate the block `n` times and generate the BPF code for the block `n` times. As this happens at compile time `n` must be a constant greater than 0 (`n > 0`). The following two probes compile into the same code: ---- interval:s:1 { unroll(3) { print("Unrolled") } } interval:s:1 { print("Unrolled") print("Unrolled") print("Unrolled") } ---- === Operators and Expressions ==== Arithmetic Operators The following operators are available for integer arithmetic: [cols="~,~"] |=== | + |integer addition | - |integer subtraction | * |integer multiplication | / |integer division | % |integer modulo |=== Operations between a signed and an unsigned integer are allowed providing bpftrace can statically prove a safe conversion is possible. If safe conversion is not guaranteed, the operation is undefined behavior and a corresponding warning will be emitted. If the two operands are different size, the smaller integer is implicitly promoted to the size of the larger one. Sign is preserved in the promotion. For example, `(uint32)5 + (uint8)3` is converted to `(uint32)5 + (uint32)3` which results in `(uint32)8`. Pointers may be used with arithmetic operators but only for addition and subtraction. For subtraction, the pointer must appear on the left side of the operator. Pointers may also be used with logical operators; they are considered true when non-null. ==== Logical Operators [cols="~,~"] |=== | && | Logical AND | \|\| | Logical OR | ! | Logical NOT |=== ==== Bitwise Operators [cols="~,~"] |=== | & | AND | \| | OR | ^ | XOR | << | Left shift the left-hand operand by the number of bits specified by the right-hand expression value | >> | Right shift the left-hand operand by the number of bits specified by the right-hand expression value |=== ==== Relational Operators The following relational operators are defined for integers and pointers. [cols="~,~"] |=== | < | left-hand expression is less than right-hand | \<= | left-hand expression is less than or equal to right-hand | > | left-hand expression is bigger than right-hand | >= | left-hand expression is bigger or equal to than right-hand | == | left-hand expression equal to right-hand | != | left-hand expression not equal to right-hand |=== The following relation operators are available for comparing strings and integer arrays. [cols="~,~"] |=== | == | left-hand string equal to right-hand | != | left-hand string not equal to right-hand |=== ==== Assignment Operators The following assignment operators can be used on both `map` and `scratch` variables: [cols="~,~"] |=== | = | Assignment, assign the right-hand expression to the left-hand variable | <\<= | Update the variable with its value left shifted by the number of bits specified by the right-hand expression value | >>= | Update the variable with its value right shifted by the number of bits specified by the right-hand expression value | += | Increment the variable by the right-hand expression value | -= | Decrement the variable by the right-hand expression value | *= | Multiple the variable by the right-hand expression value | /= | Divide the variable by the right-hand expression value | %= | Modulo the variable by the right-hand expression value | &= | Bitwise AND the variable by the right-hand expression value | \|= | Bitwise OR the variable by the right-hand expression value | ^= | Bitwise XOR the variable by the right-hand expression value |=== All these operators are syntactic sugar for combining assignment with the specified operator. `@ -= 5` is equal to `@ = @ - 5`. ==== Increment and Decrement Operators The increment (`{plus}{plus}`) and decrement (`--`) operators can be used on integer and pointer variables to increment their value by one. They can only be used on variables and can either be applied as prefix or suffix. The difference is that the expression `x{plus}{plus}` returns the original value of `x`, before it got incremented while `{plus}{plus}x` returns the value of `x` post increment. ---- $x = 10; $y = $x--; // y = 10; x = 9 $a = 10; $b = --$a; // a = 9; b = 9 ---- Note that maps will be implicitly declared and initialized to 0 if not already declared or defined. Scratch variables must be initialized before using these operators. Note `{plus}{plus}`/`--` on a shared global variable can lose updates. See <> for more details. === Preamble Preprocessor and type definitions take place in the preamble: ---- #include #define RED "\033[31m" struct S { int x; } ---- === Pointers Pointers in bpftrace are similar to those found in `C`. // TODO, not true yet === Structs `C` like structs are supported by bpftrace. Fields are accessed with the `.` operator. Fields of a pointer to a struct can be accessed with the `\->` operator. Custom structs can be defined in the preamble. Constructing structs from scratch, like `struct X var = {.f1 = 1}` in `C`, is not supported. They can only be read into a variable from a pointer. ---- struct MyStruct { int a; } kprobe:dummy { $ptr = (struct MyStruct *) arg0; $st = *$ptr; print($st.a); print($ptr->a); } ---- === Tuples bpftrace has support for immutable N-tuples (`n > 1`). A tuple is a sequence type (like an array) where, unlike an array, every element can have a different type. Tuples are a comma separated list of expressions, enclosed in brackets, `(1,2)` Individual fields can be accessed with the `.` operator. Tuples are zero indexed like arrays are. ---- interval:s:1 { $a = (1,2); $b = (3,4, $a); print($a); print($b); print($b.0); } /* * Sample output: * (1, 2) * (3, 4, (1, 2)) * 3 */ ---- === Type conversion Integer and pointer types can be converted using explicit type conversion with an expression like: ---- $y = (uint32) $z; $py = (int16 *) $pz; ---- Integer casts to a higher rank are sign extended. Conversion to a lower rank is done by zeroing leading bits. It is also possible to cast between integers and integer arrays using the same syntax: ---- $a = (uint8[8]) 12345; $x = (uint64) $a; ---- Both the cast and the destination type must have the same size. When casting to an array, it is possible to omit the size which will be determined automatically from the size of the cast value. Integers are internally represented as 64 bit signed. If you need another representation, you may cast to the supported <>. ==== Array casts It is possible to cast between integer arrays and integers. Both the source and the destination type must have the same size. The main purpose of this is to allow casts from/to byte arrays. ---- BEGIN { $a = (int8[8])12345; printf("%x %x\n", $a[0], $a[1]); printf("%d\n", (uint64)$a); } /* * Output: * 39 30 * 12345 */ ---- When casting to an array, it is possible to omit the size which will be determined automatically from the size of the cast value. This feature is especially useful when working with IP addresses since various libraries, builtins, and parts of the kernel use different approaches to represent addresses (usually byte arrays vs. integers). Array casting allows seamless comparison of such representations: ---- fentry:tcp_connect { if (args->sk->__sk_common.skc_daddr == (uint32)pton("127.0.0.1")) ... } ---- === Variables and Maps bpftrace knows two types of variables, 'scratch' and 'map'. 'scratch' variables are kept on the BPF stack and their names always start with a `$`, e.g. `$myvar`. 'scratch' variables cannot be accessed outside of their lexical block e.g. ``` $a = 1; if ($a == 1) { $b = "hello" $a = 2; } // $b is not accessible here ``` 'scratch' variables can also declared before or during initialization with `let` e.g. ``` let $a = 1; let $b; if ($a == 1) { $b = "hello" $a = 2; } // $b IS accessible here and would be an empty string if the condition wasn't true ``` If no assignment is specified variables will initialize to 0. 'map' variables use BPF 'maps'. These exist for the lifetime of `bpftrace` itself and can be accessed from all action blocks and user-space. Map names always start with a `@`, e.g. `@mymap`. All valid identifiers can be used as `name`. The data type of a variable is automatically determined during first assignment and cannot be changed afterwards. ==== Maps without Explicit Keys Values can be assigned directly to maps without a key (sometimes refered to as scalar maps). Note: you can't iterate over these maps as they don't have an accessible key. ---- @name = expression ---- ==== Map Keys Setting single value map keys. ---- @name[key] = expression ---- Map keys that are composed of multiple values are represented as tuples e.g. ---- @name[(key1,key2)] = expression ---- However, this, more concise, syntax is supported and the same as the explicit tuple above: ---- @name[key1,key2] = expression ---- Just like with any variable the type is determined on first use and cannot be modified afterwards. This applies to both the key(s) and the value type. The following snippets create a map with key signature `(int64, string[16])` and a value type of `int64`: ---- @[pid, comm]++ @[(pid, comm)]++ ---- ==== Per-Thread Variables These can be implemented as a map keyed on the thread ID. For example, `@start[tid]`: ---- kprobe:do_nanosleep { @start[tid] = nsecs; } kretprobe:do_nanosleep /has_key(@start, tid)/ { printf("slept for %d ms\n", (nsecs - @start[tid]) / 1000000); delete(@start, tid); } /* * Sample output: * slept for 1000 ms * slept for 1009 ms * slept for 2002 ms * ... */ ---- This style of map may also be useful for capturing output parameters, or other context, between two different probes. For example: ---- tracepoint:syscalls:sys_enter_wait4 { @out[tid] = args.ru; } tracepoint:syscalls:sys_exit_wait4 { $ru = @out[tid]; delete(@out, tid); if ($ru != 0) { printf("got usage ...", ...); } } ---- == Probes bpftrace supports various probe types which allow the user to attach BPF programs to different types of events. Each probe starts with a provider (e.g. `kprobe`) followed by a colon (`:`) separated list of options. The amount of options and their meaning depend on the provider and are detailed below. The valid values for options can depend on the system or binary being traced, e.g. for uprobes it depends on the binary. Also see <>. It is possible to associate multiple probes with a single action as long as the action is valid for all specified probes. Multiple probes can be specified as a comma (`,`) separated list: ---- kprobe:tcp_reset,kprobe:tcp_v4_rcv { printf("Entered: %s\n", probe); } ---- Wildcards are supported too: ---- kprobe:tcp_* { printf("Entered: %s\n", probe); } ---- Both can be combined: ---- kprobe:tcp_reset,kprobe:*socket* { printf("Entered: %s\n", probe); } ---- Most providers also support a short name which can be used instead of the full name, e.g. `kprobe:f` and `k:f` are identical. [cols="~,~,~,~"] |=== |*Probe Name* |*Short Name* |*Description* |*Kernel/User Level* | <> | - | Built-in events | Kernel/User | <> | - | Built-in events | Kernel/User | <> | `h` | Processor-level events | Kernel | <> | `i` | Timed output | Kernel/User | <> | `it` | Iterators tracing | Kernel | <> | `f`/`fr` | Kernel functions tracing with BTF support | Kernel | <> | `k`/`kr` | Kernel function start/return | Kernel | <> | `p` | Timed sampling | Kernel/User | <> | `rt` | Kernel static tracepoints with raw arguments | Kernel | <> | `s` | Kernel software events | Kernel | <> | `t` | Kernel static tracepoints | Kernel | <> | `u`/`ur` | User-level function start/return | User | <> | `U` | User-level static tracepoints | User | <> | `w`/`aw` | Memory watchpoints | Kernel |=== [#probes-begin-end] === BEGIN/END These are special built-in events provided by the bpftrace runtime. `BEGIN` is triggered before all other probes are attached. `END` is triggered after all other probes are detached. Note that specifying an `END` probe doesn't override the printing of 'non-empty' maps at exit. To prevent printing all used maps need be cleared in the `END` probe: ---- END { clear(@map1); clear(@map2); } ---- [#probes-self] === self .variants * `self:signal:SIGUSR1` These are special built-in events provided by the bpftrace runtime. The trigger function is called by the bpftrace runtime when the bpftrace process receives specific events, such as a `SIGUSR1` signal. When multiple signal handlers are attached to the same signal, only the first one is used. ---- self:signal:SIGUSR1 { print("abc"); } ---- [#probes-hardware] === hardware .variants * `hardware:event_name:` * `hardware:event_name:count` .short name * `h` These are the pre-defined hardware events provided by the Linux kernel, as commonly traced by the perf utility. They are implemented using performance monitoring counters (PMCs): hardware resources on the processor. There are about ten of these, and they are documented in the perf_event_open(2) man page. The event names are: - `cpu-cycles` or `cycles` - `instructions` - `cache-references` - `cache-misses` - `branch-instructions` or `branches` - `branch-misses` - `bus-cycles` - `frontend-stalls` - `backend-stalls` - `ref-cycles` The `count` option specifies how many events must happen before the probe fires (sampling interval). If `count` is left unspecified a default value is used. This will fire once for every 1,000,000 cache misses. ---- hardware:cache-misses:1e6 { @[pid] = count(); } ---- [#probes-interval] === interval .variants * `interval:us:count` * `interval:ms:count` * `interval:s:count` * `interval:hz:rate` .short name * `i` The interval probe fires at a fixed interval as specified by its time spec. Interval fires on one CPU at a time, unlike <> probes. This prints the rate of syscalls per second. ---- tracepoint:raw_syscalls:sys_enter { @syscalls = count(); } interval:s:1 { print(@syscalls); clear(@syscalls); } ---- [#probes-iterator] === iterator .variants * `iter:task` * `iter:task:pin` * `iter:task_file` * `iter:task_file:pin` * `iter:task_vma` * `iter:task_vma:pin` .short name * `it` **Warning** this feature is experimental and may be subject to interface changes. These are eBPF iterator probes that allow iteration over kernel objects. Iterator probe can't be mixed with any other probe, not even another iterator. Each iterator probe provides a set of fields that could be accessed with the ctx pointer. Users can display the set of available fields for each iterator via -lv options as described below. ---- iter:task { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); } /* * Sample output: * systemd:1 * kthreadd:2 * rcu_gp:3 * rcu_par_gp:4 * kworker/0:0H:6 * mm_percpu_wq:8 */ ---- ---- iter:task_file { printf("%s:%d %d:%s\n", ctx->task->comm, ctx->task->pid, ctx->fd, path(ctx->file->f_path)); } /* * Sample output: * systemd:1 1:/dev/null * systemd:1 3:/dev/kmsg * ... * su:1622 2:/dev/pts/1 * ... * bpftrace:1892 2:/dev/pts/1 * bpftrace:1892 6:anon_inode:bpf-prog */ ---- ---- iter:task_vma { printf("%s %d %lx-%lx\n", comm, pid, ctx->vma->vm_start, ctx->vma->vm_end); } /* * Sample output: * bpftrace 119480 55b92c380000-55b92c386000 * ... * bpftrace 119480 7ffd55dde000-7ffd55de2000 */ ---- It's possible to pin an iterator by specifying the optional probe ':pin' part, that defines the pin file. It can be specified as an absolute or relative path to /sys/fs/bpf. .relative pin ---- iter:task:list { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); } /* * Sample output: * Program pinned to /sys/fs/bpf/list */ ---- .absolute pin ---- iter:task_file:/sys/fs/bpf/files { printf("%s:%d %s\n", ctx->task->comm, ctx->task->pid, path(ctx->file->f_path)); } /* * Sample output: * Program pinned to /sys/fs/bpf/files */ ---- [#probes-fentry] === fentry and fexit .variants * `fentry[:module]:fn` * `fexit[:module]:fn` .short names * `f` (`fentry`) * `fr` (`fexit`) .requires (`--info`) * Kernel features:BTF * Probe types:fentry ``fentry``/``fexit`` probes attach to kernel functions similar to <>. They make use of eBPF trampolines which allow kernel code to call into BPF programs with near zero overhead. Originally, these were called `kfunc` and `kretfunc` but were later renamed to `fentry` and `fexit` to match how these are referenced in the kernel and to prevent confusion with https://docs.kernel.org/bpf/kfuncs.html[BPF Kernel Functions]. The original names are still supported for backwards compatibility. ``fentry``/``fexit`` probes make use of BTF type information to derive the type of function arguments at compile time. This removes the need for manual type casting and makes the code more resilient against small signature changes in the kernel. The function arguments are available in the `args` struct which can be inspected by doing verbose listing (see <>). These arguments are also available in the return probe (`fexit`), unlike `kretprobe`. ---- # bpftrace -lv 'fentry:tcp_reset' fentry:tcp_reset struct sock * sk struct sk_buff * skb ---- ---- fentry:x86_pmu_stop { printf("pmu %s stop\n", str(args.event->pmu->name)); } ---- The fget function takes one argument as file descriptor and you can access it via args.fd and the return value is accessible via retval: ---- fexit:fget { printf("fd %d name %s\n", args.fd, str(retval->f_path.dentry->d_name.name)); } /* * Sample output: * fd 3 name ld.so.cache * fd 3 name libselinux.so.1 */ ---- [#probes-kprobe] === kprobe and kretprobe .variants * `kprobe[:module]:fn` * `kprobe[:module]:fn+offset` * `kretprobe[:module]:fn` .short names * `k` * `kr` ``kprobe``s allow for dynamic instrumentation of kernel functions. Each time the specified kernel function is executed the attached BPF programs are ran. ---- kprobe:tcp_reset { @tcp_resets = count() } ---- Function arguments are available through the `argN` for register args. Arguments passed on stack are available using the stack pointer, e.g. `$stack_arg0 = *(int64*)reg("sp") + 16`. Whether arguments passed on stack or in a register depends on the architecture and the number or arguments used, e.g. on x86_64 the first 6 non-floating point arguments are passed in registers and all following arguments are passed on the stack. Note that floating point arguments are typically passed in special registers which don't count as `argN` arguments which can cause confusion. Consider a function with the following signature: ---- void func(int a, double d, int x) ---- Due to `d` being a floating point, `x` is accessed through `arg1` where one might expect `arg2`. bpftrace does not detect the function signature so it is not aware of the argument count or their type. It is up to the user to perform <> when needed, e.g. ---- #include #include kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } ---- Here arg0 was cast as a (struct path *), since that is the first argument to vfs_open. The struct support is the same as bcc and based on available kernel headers. This means that many, but not all, structs will be available, and you may need to manually define structs. If the kernel has BTF (BPF Type Format) data, all kernel structs are always available without defining them. For example: ---- kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); } ---- You can optionally specify a kernel module, either to include BTF data from that module, or to specify that the traced function should come from that module. ---- kprobe:kvm:x86_emulate_insn { $ctxt = (struct x86_emulate_ctxt *) arg0; printf("eip = 0x%lx\n", $ctxt->eip); } ---- See <> for more details. `kprobe` s are not limited to function entry, they can be attached to any instruction in a function by specifying an offset from the start of the function. `kretprobe` s trigger on the return from a kernel function. Return probes do not have access to the function (input) arguments, only to the return value (through `retval`). A common pattern to work around this is by storing the arguments in a map on function entry and retrieving in the return probe: ---- kprobe:d_lookup { $name = (struct qstr *)arg1; @fname[tid] = $name->name; } kretprobe:d_lookup /@fname[tid]/ { printf("%-8d %-6d %-16s M %s\n", elapsed / 1e6, pid, comm, str(@fname[tid])); } ---- [#probes-profile] === profile .variants * `profile:us:count` * `profile:ms:count` * `profile:s:count` * `profile:hz:rate` .short name * `p` Profile probes fire on each CPU on the specified interval. These operate using perf_events (a Linux kernel facility, which is also used by the perf command). ---- profile:hz:99 { @[tid] = count(); } ---- [#probes-rawtracepoint] === rawtracepoint .variants * `rawtracepoint:event` .short name * `rt` The hook point triggered by `tracepoint` and `rawtracepoint` is the same. `tracepoint` and `rawtracepoint` are nearly identical in terms of functionality. The only difference is in the program context. `rawtracepoint` offers raw arguments to the tracepoint while `tracepoint` applies further processing to the raw arguments. The additional processing is defined inside the kernel. ---- rawtracepoint:block_rq_insert { printf("%llx %llx\n", arg0, arg1); } ---- Tracepoint arguments are available via the `argN` builtins. Each arg is a 64-bit integer. The available arguments can be found in the relative path of the kernel source code `include/trace/events/`. For example: ---- include/trace/events/block.h DEFINE_EVENT(block_rq, block_rq_insert, TP_PROTO(struct request_queue *q, struct request *rq), TP_ARGS(q, rq) ); ---- [#probes-software] === software .variants * `software:event:` * `software:event:count` .short name * `s` These are the pre-defined software events provided by the Linux kernel, as commonly traced via the perf utility. They are similar to tracepoints, but there is only about a dozen of these, and they are documented in the perf_event_open(2) man page. If the count is not provided, a default is used. The event names are: - `cpu-clock` or `cpu` - `task-clock` - `page-faults` or `faults` - `context-switches` or `cs` - `cpu-migrations` - `minor-faults` - `major-faults` - `alignment-faults` - `emulation-faults` - `dummy` - `bpf-output` ---- software:faults:100 { @[comm] = count(); } ---- This roughly counts who is causing page faults, by sampling the process name for every one in one hundred faults. [#probes-tracepoint] === tracepoint .variants * `tracepoint:subsys:event` .short name * `t` Tracepoints are hooks into events in the kernel. Tracepoints are defined in the kernel source and compiled into the kernel binary which makes them a form of static tracing. Unlike `kprobe` s, new tracepoints cannot be added without modifying the kernel. The advantage of tracepoints is that they generally provide a more stable interface than `kprobe` s do, they do not depend on the existence of a kernel function. ---- tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args.filename)); } ---- Tracepoint arguments are available in the `args` struct which can be inspected with verbose listing, see the <> section for more details. ---- # bpftrace -lv "tracepoint:*" tracepoint:xhci-hcd:xhci_setup_device_slot u32 info u32 info2 u32 tt_info u32 state ... ---- Alternatively members for each tracepoint can be listed from their /format file in /sys. Apart from the filename member, we can also print flags, mode, and more. After the "common" members listed first, the members are specific to the tracepoint. .Additional information * https://www.kernel.org/doc/html/latest/trace/tracepoints.html [#probes-uprobe] === uprobe, uretprobe .variants * `uprobe:binary:func` * `uprobe:binary:func+offset` * `uprobe:binary:offset` * `uretprobe:binary:func` .short names * `u` * `ur` `uprobe` s or user-space probes are the user-space equivalent of `kprobe` s. The same limitations that apply <> also apply to `uprobe` s and `uretprobe` s, namely: arguments are available via the `argN` and `sargN` builtins and can only be accessed with a uprobe (`sargN` is more common for older versions of golang). retval is the return value for the instrumented function and can only be accessed with a uretprobe. ---- uprobe:/bin/bash:readline { printf("arg0: %d\n", arg0); } ---- What does arg0 of readline() in /bin/bash contain? I don't know, so I'll need to look at the bash source code to find out what its arguments are. When tracing libraries, it is sufficient to specify the library name instead of a full path. The path will be then automatically resolved using `/etc/ld.so.cache`: ---- uprobe:libc:malloc { printf("Allocated %d bytes\n", arg0); } ---- If the traced binary has DWARF included, function arguments are available in the `args` struct which can be inspected with verbose listing, see the <> section for more details. ---- # bpftrace -lv 'uprobe:/bin/bash:rl_set_prompt' uprobe:/bin/bash:rl_set_prompt const char* prompt ---- When tracing C{plus}{plus} programs, it's possible to turn on automatic symbol demangling by using the `:cpp` prefix: ---- # bpftrace:cpp:"bpftrace::BPFtrace::add_probe" { ... } ---- It is important to note that for `uretprobe` s to work the kernel runs a special helper on user-space function entry which overrides the return address on the stack. This can cause issues with languages that have their own runtime like Golang: .example.go ---- func myprint(s string) { fmt.Printf("Input: %s\n", s) } func main() { ss := []string{"a", "b", "c"} for _, s := range ss { go myprint(s) } time.Sleep(1*time.Second) } ---- .bpftrace ---- # bpftrace -e 'uretprobe:./test:main.myprint { @=count(); }' -c ./test runtime: unexpected return pc for main.myprint called from 0x7fffffffe000 stack: frame={sp:0xc00008cf60, fp:0xc00008cfd0} stack=[0xc00008c000,0xc00008d000) fatal error: unknown caller pc ---- [#probes-usdt] === usdt .variants * `usdt:binary_path:probe_name` * `usdt:binary_path:[probe_namespace]:probe_name` * `usdt:library_path:probe_name` * `usdt:library_path:[probe_namespace]:probe_name` .short name * `U` Where probe_namespace is optional if probe_name is unique within the binary. You can target the entire host (or an entire process's address space by using the `-p` arg) by using a single wildcard in place of the binary_path/library_path: ---- usdt:*:loop { printf("hi\n"); } ---- Please note that if you use wildcards for the probe_name or probe_namespace and end up targeting multiple USDTs for the same probe you might get errors if you also utilize the USDT argument builtin (e.g. arg0) as they could be of different types. Arguments are available via the `argN` builtins: ---- usdt:/root/tick:loop { printf("%s: %d\n", str(arg0), arg1); } ---- bpftrace also supports USDT semaphores. If both your environment and bpftrace support uprobe refcounts, then USDT semaphores are automatically activated for all processes upon probe attachment (and --usdt-file-activation becomes a noop). You can check if your system supports uprobe refcounts by running: ---- # bpftrace --info 2>&1 | grep "uprobe refcount" bcc bpf_attach_uprobe refcount: yes uprobe refcount (depends on Build:bcc bpf_attach_uprobe refcount): yes ---- If your system does not support uprobe refcounts, you may activate semaphores by passing in -p $PID or --usdt-file-activation. --usdt-file-activation looks through /proc to find processes that have your probe's binary mapped with executable permissions into their address space and then tries to attach your probe. Note that file activation occurs only once (during attach time). In other words, if later during your tracing session a new process with your executable is spawned, your current tracing session will not activate the new process. Also note that --usdt-file-activation matches based on file path. This means that if bpftrace runs from the root host, things may not work as expected if there are processes execved from private mount namespaces or bind mounted directories. One workaround is to run bpftrace inside the appropriate namespaces (i.e. the container). [#probes-watchpoint] === watchpoint and asyncwatchpoint .variants * `watchpoint:absolute_address:length:mode` * `watchpoint:function+argN:length:mode` .short names * `w` * `aw` This feature is experimental and may be subject to interface changes. Memory watchpoints are also architecture dependent. These are memory watchpoints provided by the kernel. Whenever a memory address is written to (`w`), read from (`r`), or executed (`x`), the kernel can generate an event. In the first form, an absolute address is monitored. If a pid (`-p`) or a command (`-c`) is provided, bpftrace takes the address as a userspace address and monitors the appropriate process. If not, bpftrace takes the address as a kernel space address. In the second form, the address present in `argN` when `function` is entered is monitored. A pid or command must be provided for this form. If synchronous (`watchpoint`), a `SIGSTOP` is sent to the tracee upon function entry. The tracee will be ``SIGCONT``ed after the watchpoint is attached. This is to ensure events are not missed. If you want to avoid the `SIGSTOP` + `SIGCONT` use `asyncwatchpoint`. Note that on most architectures you may not monitor for execution while monitoring read or write. ---- # bpftrace -e 'watchpoint:0x10000000:8:rw { printf("hit!\n"); }' -c ./testprogs/watchpoint ---- Print the call stack every time the `jiffies` variable is updated: ---- watchpoint:0x$(awk '$3 == "jiffies" {print $1}' /proc/kallsyms):8:w { @[kstack] = count(); } ---- "hit" and exit when the memory pointed to by `arg1` of `increment` is written to: [,C] ---- # cat wpfunc.c #include #include #include __attribute__((noinline)) void increment(__attribute__((unused)) int _, int *i) { (*i)++; } int main() { int *i = malloc(sizeof(int)); while (1) { increment(0, i); (*i)++; usleep(1000); } } ---- ---- # bpftrace -e 'watchpoint:increment+arg1:4:w { printf("hit!\n"); exit() }' -c ./wpfunc ---- == Builtins Builtins are special variables built into the language. Unlike scratch and map variables they don't need a `$` or `@` as prefix (except for the positional parameters). The 'Kernel' column indicates the minimum kernel version required and the 'BPF Helper' column indicates the raw BPF helper function used for this builtin. [%header] |=== | Variable | Type | Kernel | BPF Helper | Description | <> | int64 | n/a | n/a | The nth positional parameter passed to the bpftrace program. If less than n parameters are passed this evaluates to `0`. For string arguments use the `str()` call to retrieve the value. | `$#` | int64 | n/a | n/a | Total amount of positional parameters passed. | `arg0`, `arg1`, `...argn` | int64 | n/a | n/a | nth argument passed to the function being traced. These are extracted from the CPU registers. The amount of args passed in registers depends on the CPU architecture. (kprobes, uprobes, usdt). | `args` | struct args | n/a | n/a | The struct of all arguments of the traced function. Available in `tracepoint`, `fentry`, `fexit`, and `uprobe` (with DWARF) probes. Use `args.x` to access argument `x` or `args` to get a record with all arguments. | cgroup | uint64 | 4.18 | get_current_cgroup_id | ID of the cgroup the current process belongs to. Only works with cgroupv2. | comm | string[16] | 4.2 | get_current_comm | Name of the current thread | cpid | uint32 | n/a | n/a | Child process ID, if bpftrace is invoked with `-c` | cpu | uint32 | 4.1 | raw_smp_processor_id | ID of the processor executing the BPF program | curtask | uint64 | 4.8 | get_current_task | Pointer to `struct task_struct` of the current task | elapsed | uint64 | (see nsec) | ktime_get_ns / ktime_get_boot_ns | Nanoseconds elapsed since bpftrace initialization, based on `nsecs` | func | string | n/a | n/a | Name of the current function being traced (kprobes,uprobes) | gid | uint64 | 4.2 | get_current_uid_gid | Group ID of the current thread, as seen from the init namespace | jiffies | uint64 | 5.9 | get_jiffies_64 | Jiffies of the kernel. In 32-bit system, using this builtin might be slower. | numaid | uint32 | 5.8 | numa_node_id | ID of the NUMA node executing the BPF program | pid | uint32 | 4.2 | get_current_pid_tgid | Process ID of the current thread (aka thread group ID), as seen from the init namespace | probe | string | n/na | n/a | Name of the current probe | rand | uint32 | 4.1 | get_prandom_u32 | Random number | return | n/a | n/a | n/a | The return keyword is used to exit the current probe. This differs from exit() in that it doesn't exit bpftrace. | retval | uint64 | n/a | n/a | Value returned by the function being traced (kretprobe, uretprobe, fexit). For kretprobe and uretprobe, its type is `uint64`, but for fexit it depends. You can look up the type using `bpftrace -lv` | tid | uint32 | 4.2 | get_current_pid_tgid | Thread ID of the current thread, as seen from the init namespace | uid | uint64 | 4.2 | get_current_uid_gid | User ID of the current thread, as seen from the init namespace |=== [#builtins-positional-parameters] === Positional Parameters .variants * `$1`, `$2`, ..., `$N`, `$#` These are the positional parameters to the bpftrace program, also referred to as command line arguments. If the parameter is numeric (entirely digits), it can be used as a number. If it is non-numeric, it must be used as a string in the `str()` call. If a parameter is used that was not provided, it will default to zero for numeric context, and "" for string context. Positional parameters may also be used in probe argument and will be treated as a string parameter. If a positional parameter is used in `str()`, it is interpreted as a pointer to the actual given string literal, which allows to do pointer arithmetic on it. Only addition of a single constant, less or equal to the length of the supplied string, is allowed. `$#` returns the number of positional arguments supplied. This allows scripts to be written that use basic arguments to change their behavior. If you develop a script that requires more complex argument processing, it may be better suited for bcc instead, which supports Python's argparse and completely custom argument processing. ---- # bpftrace -e 'BEGIN { printf("I got %d, %s (%d args)\n", $1, str($2), $#); }' 42 "hello" I got 42, hello (2 args) # bpftrace -e 'BEGIN { printf("%s\n", str($1 + 1)) }' "hello" ello ---- Script example, bsize.bt: ---- #!/usr/local/bin/bpftrace BEGIN { printf("Tracing block I/O sizes > %d bytes\n", $1); } tracepoint:block:block_rq_issue /args.bytes > $1/ { @ = hist(args.bytes); } ---- When run with a 65536 argument: ---- # ./bsize.bt 65536 Tracing block I/O sizes > 65536 bytes ^C @: [512K, 1M) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| ---- It has passed the argument in as `$1` and used it as a filter. With no arguments, `$1` defaults to zero: ---- # ./bsize.bt Attaching 2 probes... Tracing block I/O sizes > 0 bytes ^C @: [4K, 8K) 115 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 35 |@@@@@@@@@@@@@@@ | [16K, 32K) 5 |@@ | [32K, 64K) 3 |@ | [64K, 128K) 1 | | [128K, 256K) 0 | | [256K, 512K) 0 | | [512K, 1M) 1 | | ---- == Functions [%header] |=== | Name | Description | Sync/Async/Compile Time | <> | Reverse byte order | Sync | <> | Returns a hex-formatted string of the data pointed to by d | Sync | <> | Print file content | Async | <> | Resolve cgroup ID | Compile Time | <> | Convert cgroup id to cgroup path | Sync | <> | Quit bpftrace with an optional exit code | Async | <> | Print the array | Async | <> | Resolve kernel symbol name | Compile Time | <> | Annotate as kernelspace pointer | Sync | <> | Kernel stack trace | Sync | <> | Resolve kernel address | Async | <> | Count ustack/kstack frames | Sync | <> | Convert MAC address data | Sync | <> | Timestamps and Time Deltas | Sync | <> | Convert IP address data to text | Sync | <> | Offset of element in structure | Compile Time | <> | Override return value | Sync | <> | Return full path | Sync | <> | Resolve percpu kernel symbol name | Sync | <> | Print a non-map value with default formatting | Async | <> | Print formatted | Async | <> | Convert text IP address to byte array | Compile Time | <> | Returns the value stored in the named register | Sync | <> | Send a signal to the current process | Sync | <> | Return size of a type or expression | Sync | <> | Write skb 's data section into a PCAP file | Async | <> | Returns the string pointed to by s | Sync | <> | Compares whether the string haystack contains the string needle. | Sync | <> | Get error message for errno code | Sync | <> | Return a formatted timestamp | Async | <> | Compare first n characters of two strings | Sync | <> | Execute shell command | Async | <> | Print formatted time | Async | <> | Resolve user-level symbol name | Compile Time | <> | Annotate as userspace pointer | Sync | <> | User stack trace | Sync | <> | Resolve user space address | Async |=== Functions that are marked *async* are asynchronous which can lead to unexpected behaviour, see the <> section for more information. *compile time* functions are evaluated at compile time, a static value will be compiled into the program. *unsafe* functions can have dangerous side effects and should be used with care, the `--unsafe` flag is required for use. [#functions-bswap] === 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`. [#functions-buf] === buf .variants * `buffer buf(void * data, [int64 length])` `buf` reads `length` amount of bytes from address `data`. The maximum value of `length` is limited to the `BPFTRACE_MAX_STRLEN` variable. For arrays the `length` is optional, it is automatically inferred from the signature. `buf` is address space aware and will call the correct helper based on the address space associated with `data`. The `buffer` 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`). The `%rx` format specifier can be used to print everything in hex form, including ASCII characters. The similar `%rh` format specifier prints everything in hex form without `\x` and with spaces between bytes (e.g. `0a fe`). ---- interval:s:1 { printf("%r\n", buf(kaddr("avenrun"), 8)); } ---- ---- \x00\x03\x00\x00\x00\x00\x00\x00 \xc2\x02\x00\x00\x00\x00\x00\x00 ---- [#functions-cat] === cat .variants * `void cat(string namefmt, [...args])` *async* Dump the contents of the named file to stdout. `cat` supports the same format string and arguments that `printf` does. If the file cannot be opened or read an error is printed to stderr. ---- tracepoint:syscalls:sys_enter_execve { cat("/proc/%d/maps", pid); } ---- ---- 55f683ebd000-55f683ec1000 r--p 00000000 08:01 1843399 /usr/bin/ls 55f683ec1000-55f683ed6000 r-xp 00004000 08:01 1843399 /usr/bin/ls 55f683ed6000-55f683edf000 r--p 00019000 08:01 1843399 /usr/bin/ls 55f683edf000-55f683ee2000 rw-p 00021000 08:01 1843399 /usr/bin/ls 55f683ee2000-55f683ee3000 rw-p 00000000 00:00 0 ---- [#functions-cgroupid] === cgroupid .variants * `uint64 cgroupid(const string path)` *compile time* `cgroupid` retrieves the cgroupv2 ID of the cgroup available at `path`. ---- BEGIN { print(cgroupid("/sys/fs/cgroup/system.slice")); } ---- [#functions-cgroup_path] === cgroup_path .variants * `cgroup_path_t 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 */ } ---- [#functions-exit] === exit .variants * `void exit([int code])` *async* Terminate bpftrace, as if a `SIGTERM` was received. The `END` probe will still trigger (if specified) and maps will be printed. An optional exit code can be provided. ---- BEGIN { exit(); } ---- Or ---- BEGIN { exit(1); } ---- [#functions-join] === join .variants * `void join(char *arr[], [char * sep = ' '])` *async* `join` joins all the string array `arr` with `sep` as separator into one string. This string will be printed to stdout directly, it cannot be used as string value. The concatenation of the array members is done in BPF and the printing happens in userspace. ---- tracepoint:syscalls:sys_enter_execve { join(args.argv); } ---- [#functions-kaddr] === kaddr .variants * `uint64 kaddr(const string name)` *compile time* Get the address of the kernel symbol `name`. ---- interval:s:1 { $avenrun = kaddr("avenrun"); $load1 = *$avenrun; } ---- You can find all kernel symbols at `/proc/kallsyms`. [#functions-kptr] === kptr .variants * `T * kptr(T * ptr)` Marks `ptr` as a kernel address space pointer. See the address-spaces section for more information on address-spaces. The pointer type is left unchanged. [#functions-kstack] === kstack .variants * `kstack_t kstack([StackMode mode, ][int limit])` These are implemented using BPF stack maps. ---- kprobe:ip_output { @[kstack()] = count(); } /* * Sample output: * @[ * ip_output+1 * tcp_transmit_skb+1308 * tcp_write_xmit+482 * tcp_release_cb+225 * release_sock+64 * tcp_sendmsg+49 * sock_sendmsg+48 * sock_write_iter+135 * __vfs_write+247 * vfs_write+179 * sys_write+82 * entry_SYSCALL_64_fastpath+30 * ]: 1708 */ ---- Sampling only three frames from the stack (limit = 3): ---- kprobe:ip_output { @[kstack(3)] = count(); } /* * Sample output: * @[ * ip_output+1 * tcp_transmit_skb+1308 * tcp_write_xmit+482 * ]: 1708 */ ---- You can also choose a different output format. Available formats are `bpftrace`, `perf`, and `raw` (no symbolication): ---- kprobe:ip_output { @[kstack(perf, 3)] = count(); } /* * Sample output: * @[ * ffffffffb4019501 do_mmap+1 * ffffffffb401700a sys_mmap_pgoff+266 * ffffffffb3e334eb sys_mmap+27 * ]: 1708 */ ---- [#functions-ksym] === ksym .variants * `ksym_t ksym(uint64 addr)` *async* Retrieve the name of the function that contains address `addr`. The address to name mapping happens in user-space. The `ksym_t` type can be printed with the `%s` format specifier. ---- kprobe:do_nanosleep { printf("%s\n", ksym(reg("ip"))); } /* * Sample output: * do_nanosleep */ ---- [#functions-len] === len .variants * `int64 len(ustack stack)` * `int64 len(kstack stack)` Retrieve the depth (measured in # of frames) of the call stack specified by `stack`. [#functions-macaddr] === macaddr .variants * `macaddr_t macaddr(char [6] mac)` Create a buffer that holds a macaddress as read from `mac` This buffer can be printed in the canonical string format using the `%s` format specifier. ---- kprobe:arp_create { $stack_arg0 = *(uint8*)(reg("sp") + 8); $stack_arg1 = *(uint8*)(reg("sp") + 16); printf("SRC %s, DST %s\n", macaddr($stack_arg0), macaddr($stack_arg1)); } /* * Sample output: * SRC 18:C0:4D:08:2E:BB, DST 74:83:C2:7F:8C:FF */ ---- [#functions-nsecs] === nsecs .variants * `timestamp nsecs([TimestampMode mode])` Returns a timestamp in nanoseconds, as given by the requested kernel clock. Defaults to `boot` if no clock is explicitly requested. - `nsecs(monotonic)` - nanosecond timestamp since boot, exclusive of time the system spent suspended (CLOCK_MONOTONIC) - `nsecs(boot)` - nanoseconds since boot, inclusive of time the system spent suspended (CLOCK_BOOTTIME) - `nsecs(tai)` - TAI timestamp in nanoseconds (CLOCK_TAI) - `nsecs(sw_tai)` - approximation of TAI timestamp in nanoseconds, is obtained through the "triple vdso sandwich" method. For older kernels without direct TAI timestamp access in BPF. ---- interval:s:1 { $sw_tai1 = nsecs(sw_tai); $tai = nsecs(tai); $sw_tai2 = nsecs(sw_tai); printf("sw_tai precision: %lldns\n", ($sw_tai1 + $sw_tai2)/2 - $tai); } /* * Sample output: * sw_tai precision: -98ns * sw_tai precision: -99ns * ... */ ---- [#functions-ntop] === ntop .variants * `inet ntop([int64 af, ] int addr)` * `inet ntop([int64 af, ] char addr[4])` * `inet ntop([int64 af, ] char addr[16])` `ntop` returns the string representation of an IPv4 or IPv6 address. `ntop` will infer the address type (IPv4 or IPv6) based on the `addr` type and size. If an integer or `char[4]` is given, ntop assumes IPv4, if a `char[16]` is given, ntop assumes IPv6. You can also pass the address type (e.g. AF_INET) explicitly as the first parameter. [#functions-offsetof] === offsetof .variants * `uint64 offsetof(STRUCT, FIELD[.SUBFIELD])` * `uint64 offsetof(EXPRESSION, FIELD[.SUBFIELD])` *compile time* Returns offset of the field offset bytes in struct. Similar to kernel `offsetof` operator. Support any number of sub field levels, for example: ---- struct Foo { struct { struct { struct { int d; } c; } b; } a; } BEGIN { @x = offsetof(struct Foo, a.b.c.d); exit(); } ---- [#functions-override] === override .variants * `void override(uint64 rc)` *unsafe* *Kernel* 4.16 *Helper* `bpf_override` .Supported probes * kprobe When using `override` the probed function will not be executed and instead `rc` will be returned. ---- kprobe:__x64_sys_getuid /comm == "id"/ { override(2<<21); } ---- ---- uid=4194304 gid=0(root) euid=0(root) groups=0(root) ---- This feature only works on kernels compiled with `CONFIG_BPF_KPROBE_OVERRIDE` and only works on functions tagged `ALLOW_ERROR_INJECTION`. bpftrace does not test whether error injection is allowed for the probed function, instead if will fail to load the program into the kernel: ---- ioctl(PERF_EVENT_IOC_SET_BPF): Invalid argument Error attaching probe: 'kprobe:vfs_read' ---- [#functions-path] === path .variants * `char * path(struct path * path [, int32 size])` *Kernel* 5.10 *Helper* `bpf_d_path` Return full path referenced by struct path pointer in argument. If `size` is set, the path will be clamped by `size` otherwise `BPFTRACE_MAX_STRLEN` is used. If `size` is smaller than the resolved path, the resulting string will be truncated at the front rather than at the end. This function can only be used by functions that are allowed to, these functions are contained in the `btf_allowlist_d_path` set in the kernel. [#functions-percpu-kaddr] === percpu_kaddr .variants * `void *percpu_kaddr(const string name)` * `void *percpu_kaddr(const string name, int cpu)` *sync* Get the address of the percpu kernel symbol `name` for CPU `cpu`. When `cpu` is omitted, the current CPU is used. ---- interval:s:1 { $proc_cnt = percpu_kaddr("process_counts"); printf("% processes are running on CPU %d\n", *$proc_cnt, cpu); } ---- The second variant may return NULL if `cpu` is higher than the number of available CPUs. Therefore, it is necessary to perform a NULL-check on the result when accessing fields of the pointed structure, otherwise the BPF program will be rejected. ---- interval:s:1 { $runqueues = (struct rq *)percpu_kaddr("runqueues", 0); if ($runqueues != 0) { // The check is mandatory here print($runqueues->nr_running); } } ---- [#functions-print] === print .variants * `void print(T val)` *async* .variants * `void print(T val)` * `void print(@map)` * `void print(@map, uint64 top)` * `void print(@map, uint64 top, uint64 div)` `print` prints a the value, which can be a map or a scalar value, with the default formatting for the type. ---- interval:s:1 { print(123); print("abc"); exit(); } /* * Sample output: * 123 * abc */ ---- ---- interval:ms:10 { @=hist(rand); } interval:s:1 { print(@); exit(); } ---- Prints: ---- @: [16M, 32M) 3 |@@@ | [32M, 64M) 2 |@@ | [64M, 128M) 1 |@ | [128M, 256M) 4 |@@@@ | [256M, 512M) 3 |@@@ | [512M, 1G) 14 |@@@@@@@@@@@@@@ | [1G, 2G) 22 |@@@@@@@@@@@@@@@@@@@@@@ | [2G, 4G) 51 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| ---- Declared maps and histograms are automatically printed out on program termination. Note that maps are printed by reference while scalar values are copied. This means that updating and printing maps in a fast loop will likely result in bogus map values as the map will be updated before userspace gets the time to dump and print it. The printing of maps supports the optional `top` and `div` arguments. `top` limits the printing to the top N entries with the highest integer values ---- BEGIN { $i = 11; while($i) { @[$i] = --$i; } print(@, 2); clear(@); exit() } /* * Sample output: * @[9]: 9 * @[10]: 10 */ ---- The `div` argument scales the values prior to printing them. Scaling values before storing them can result in rounding errors. Consider the following program: ---- kprobe:f { @[func] += arg0/10; } ---- With the following sequence as numbers for arg0: `134, 377, 111, 99`. The total is `721` which rounds to `72` when scaled by 10 but the program would print `70` due to the rounding of individual values. Changing the print call to `print(@, 5, 2)` will take the top 5 values and scale them by 2: ---- @[6]: 3 @[7]: 3 @[8]: 4 @[9]: 4 @[10]: 5 ---- [#functions-printf] === printf .variants * `void printf(const string fmt, args...)` *async* `printf()` formats and prints data. It behaves similar to `printf()` found in `C` and many other languages. The format string has to be a constant, it cannot be modified at runtime. The formatting of the string happens in user space. Values are copied and passed by value. bpftrace supports all the typical format specifiers like `%llx` and `%hhu`. The non-standard ones can be found in the table below: [%header] |=== | Specifier | Type | Description | r | buffer | Hex-formatted string to print arbitrary binary content returned by the <> function. | rh | buffer | Prints in hex-formatted string without `\x` and with spaces between bytes (e.g. `0a fe`) |=== `printf()` can also symbolize enums as strings. User defined enums as well as enums defined in the kernel are supported. For example: ---- enum custom { CUSTOM_ENUM = 3, }; BEGIN { $r = SKB_DROP_REASON_SOCKET_FILTER; printf("%d, %s, %s\n", $r, $r, CUSTOM_ENUM); exit(); } ---- yields: ---- 6, SKB_DROP_REASON_SOCKET_FILTER, CUSTOM_ENUM ---- Colors are supported too, using standard terminal escape sequences: ---- print("\033[31mRed\t\033[33mYellow\033[0m\n") ---- [#functions-pton] === pton .variants * `char addr[4] pton(const string *addr_v4)` * `char addr[16] pton(const string *addr_v6)` *compile time* `pton` converts a text representation of an IPv4 or IPv6 address to byte array. `pton` infers the address family based on `.` or `:` in the given argument. `pton` comes in handy when we need to select packets with certain IP addresses. [#functions-reg] === reg .variants * `uint64 reg(const string name)` .Supported probes * kprobe * uprobe Get the contents of the register identified by `name`. Valid names depend on the CPU architecture. [#functions-signal] === signal .variants * `void signal(const string sig)` * `void signal(uint32 signum)` *unsafe* *Kernel* 5.3 *Helper* `bpf_send_signal` Probe types: k(ret)probe, u(ret)probe, USDT, profile Send a signal to the process being traced. The signal can either be identified by name, e.g. `SIGSTOP` or by ID, e.g. `19` as found in `kill -l`. ---- kprobe:__x64_sys_execve /comm == "bash"/ { signal(5); } ---- ---- $ ls Trace/breakpoint trap (core dumped) ---- [#functions-sizeof] === sizeof .variants * `uint64 sizeof(TYPE)` * `uint64 sizeof(EXPRESSION)` *compile time* Returns size of the argument in bytes. Similar to C/C++ `sizeof` operator. Note that the expression does not get evaluated. [#functions-skboutput] === skboutput .variants * `uint32 skboutput(const string path, struct sk_buff *skb, uint64 length, const uint64 offset)` *Kernel* 5.5 *Helper* bpf_skb_output Write sk_buff `skb` 's data section to a PCAP file in the `path`, starting from `offset` to `offset` + `length`. The PCAP file is encapsulated in RAW IP, so no ethernet header is included. The `data` section in the struct `skb` may contain ethernet header in some kernel contexts, you may set `offset` to 14 bytes to exclude ethernet header. Each packet's timestamp is determined by adding `nsecs` and boot time, the accuracy varies on different kernels, see `nsecs`. This function returns 0 on success, or a negative error in case of failure. Environment variable `BPFTRACE_PERF_RB_PAGES` should be increased in order to capture large packets, or else these packets will be dropped. Usage ---- # cat dump.bt fentry:napi_gro_receive { $ret = skboutput("receive.pcap", args.skb, args.skb->len, 0); } fentry:dev_queue_xmit { // setting offset to 14, to exclude ethernet header $ret = skboutput("output.pcap", args.skb, args.skb->len, 14); printf("skboutput returns %d\n", $ret); } # export BPFTRACE_PERF_RB_PAGES=1024 # bpftrace dump.bt ... # tcpdump -n -r ./receive.pcap | head -3 reading from file ./receive.pcap, link-type RAW (Raw IP) dropped privs to tcpdump 10:23:44.674087 IP 22.128.74.231.63175 > 192.168.0.23.22: Flags [.], ack 3513221061, win 14009, options [nop,nop,TS val 721277750 ecr 3115333619], length 0 10:23:45.823194 IP 100.101.2.146.53 > 192.168.0.23.46619: 17273 0/1/0 (130) 10:23:45.823229 IP 100.101.2.146.53 > 192.168.0.23.46158: 45799 1/0/0 A 100.100.45.106 (60) ---- [#functions-str] === str .variants * `string str(char * data [, uint32 length)` *Helper* `probe_read_str, probe_read_{kernel,user}_str` `str` reads a NULL terminated (`\0`) string from `data`. The maximum string length is limited by the `BPFTRACE_MAX_STRLEN` env variable, unless `length` is specified and shorter than the maximum. In case the string is longer than the specified length only `length - 1` bytes are copied and a NULL byte is appended at the end. When available (starting from kernel 5.5, see the `--info` flag) bpftrace will automatically use the `kernel` or `user` variant of `probe_read_{kernel,user}_str` based on the address space of `data`, see <> for more information. [#functions-strcontains] === strcontains .variants * `int64 strcontains(const char *haystack, const char *needle)` `strcontains` compares whether the string haystack contains the string needle. If needle is contained `1` is returned, else zero is returned. bpftrace doesn't read past the length of the shortest string. [#functions-strerror] === strerror .variants * `strerror_t strerror(int error)` Convert errno code to string. This is done asynchronously in userspace when the strerror value is printed, hence the returned value can only be used for printing. ---- #include BEGIN { print(strerror(EPERM)); } ---- [#functions-strftime] === strftime .variants * `timestamp strftime(const string fmt, int64 timestamp_ns)` *async* Format the nanoseconds since boot timestamp `timestamp_ns` according to the format specified by `fmt`. The time conversion and formatting happens in user space, therefore the `timestamp` value returned can only be used for printing using the `%s` format specifier. bpftrace uses the `strftime(3)` function for formatting time and supports the same format specifiers. ---- interval:s:1 { printf("%s\n", strftime("%H:%M:%S", nsecs)); } ---- bpftrace also supports the following format string extensions: [%header] |=== | Specifier | Description | `%f` | Microsecond as a decimal number, zero-padded on the left |=== [#functions-strncmp] === strncmp .variants * `int64 strncmp(char * s1, char * s2, int64 n)` `strncmp` compares up to `n` characters string `s1` and string `s2`. If they're equal `0` is returned, else a non-zero value is returned. bpftrace doesn't read past the length of the shortest string. The use of the `==` and `!=` operators is recommended over calling `strncmp` directly. [#functions-system] === system .variants * `void system(string namefmt [, ...args])` *unsafe* *async* `system` lets bpftrace run the specified command (`fork` and `exec`) until it completes and print its stdout. The `command` is run with the same privileges as bpftrace and it blocks execution of the processing threads which can lead to missed events and delays processing of async events. ---- interval:s:1 { time("%H:%M:%S: "); printf("%d\n", @++); } interval:s:10 { system("/bin/sleep 10"); } interval:s:30 { exit(); } ---- Note how the async `time` and `printf` first print every second until the `interval:s:10` probe hits, then they print every 10 seconds due to bpftrace blocking on `sleep`. ---- Attaching 3 probes... 08:50:37: 0 08:50:38: 1 08:50:39: 2 08:50:40: 3 08:50:41: 4 08:50:42: 5 08:50:43: 6 08:50:44: 7 08:50:45: 8 08:50:46: 9 08:50:56: 10 08:50:56: 11 08:50:56: 12 08:50:56: 13 08:50:56: 14 08:50:56: 15 08:50:56: 16 08:50:56: 17 08:50:56: 18 08:50:56: 19 ---- `system` supports the same format string and arguments that `printf` does. ---- tracepoint:syscalls:sys_enter_execve { system("/bin/grep %s /proc/%d/status", "vmswap", pid); } ---- [#functions-time] === time .variants * `void time(const string fmt)` *async* Format the current wall time according to the format specifier `fmt` and print it to stdout. Unlike `strftime()` `time()` doesn't send a timestamp from the probe, instead it is the time at which user-space processes the event. bpftrace uses the `strftime(3)` function for formatting time and supports the same format specifiers. [#functions-uaddr] === uaddr .variants * `T * uaddr(const string sym)` .Supported probes * uprobes * uretprobes * USDT **Does not work with ASLR, see issue link:https://github.com/bpftrace/bpftrace/issues/75[#75]** The `uaddr` function returns the address of the specified symbol. This lookup happens during program compilation and cannot be used dynamically. The default return type is `uint64*`. If the ELF object size matches a known integer size (1, 2, 4 or 8 bytes) the return type is modified to match the width (`uint8*`, `uint16*`, `uint32*` or `uint64*` resp.). As ELF does not contain type info the type is always assumed to be unsigned. ---- uprobe:/bin/bash:readline { printf("PS1: %s\n", str(*uaddr("ps1_prompt"))); } ---- [#functions-uptr] === uptr .variants * `T * uptr(T * ptr)` Marks `ptr` as a user address space pointer. See the address-spaces section for more information on address-spaces. The pointer type is left unchanged. [#functions-ustack] === ustack .variants * `ustack_t ustack([StackMode mode, ][int limit])` These are implemented using BPF stack maps. ---- kprobe:do_sys_open /comm == "bash"/ { @[ustack()] = count(); } /* * Sample output: * @[ * __open_nocancel+65 * command_word_completion_function+3604 * rl_completion_matches+370 * bash_default_completion+540 * attempt_shell_completion+2092 * gen_completion_matches+82 * rl_complete_internal+288 * rl_complete+145 * _rl_dispatch_subseq+647 * _rl_dispatch+44 * readline_internal_char+479 * readline_internal_charloop+22 * readline_internal+23 * readline+91 * yy_readline_get+152 * yy_readline_get+429 * yy_getc+13 * shell_getc+469 * read_token+251 * yylex+192 * yyparse+777 * parse_command+126 * read_command+207 * reader_loop+391 * main+2409 * __libc_start_main+231 * 0x61ce258d4c544155 * ]: 9 */ ---- Sampling only three frames from the stack (limit = 3): ---- kprobe:ip_output { @[ustack(3)] = count(); } /* * Sample output: * @[ * __open_nocancel+65 * command_word_completion_function+3604 * rl_completion_matches+370 * ]: 20 */ ---- You can also choose a different output format. Available formats are `bpftrace`, `perf`, and `raw` (no symbolication): ---- kprobe:ip_output { @[ustack(perf, 3)] = count(); } /* * Sample output: * @[ * 5649feec4090 readline+0 (/home/mmarchini/bash/bash/bash) * 5649fee2bfa6 yy_readline_get+451 (/home/mmarchini/bash/bash/bash) * 5649fee2bdc6 yy_getc+13 (/home/mmarchini/bash/bash/bash) * ]: 20 */ ---- Note that for these examples to work, bash had to be recompiled with frame pointers. [#functions-usym] === usym .variants * `usym_t usym(uint64 * addr)` *async* .Supported probes * uprobes * uretprobes Equal to <> but resolves user space symbols. If ASLR is enabled, user space symbolication only works when the process is running at either the time of the symbol resolution or the time of the probe attachment. The latter requires `BPFTRACE_CACHE_USER_SYMBOLS` to be set to `PER_PID`, and might not work with older versions of BCC. A similar limitation also applies to dynamically loaded symbols. ---- uprobe:/bin/bash:readline { printf("%s\n", usym(reg("ip"))); } /* * Sample output: * readline */ ---- [#functions-unwatch] === unwatch .variants * `void unwatch(void * addr)` *async* Removes a watchpoint == Map Functions Map functions are built-in functions who's return value can only be assigned to maps. The data type associated with these functions are only for internal use and are not compatible with the (integer) operators. Functions that are marked *async* are asynchronous which can lead to unexpected behavior, see the <> section for more information. See <> for more information on <>. [%header] |=== | Name | Description | Sync/async | <> | Calculate the running average of `n` between consecutive calls. | Sync | <> | Clear all keys/values from a map. | Async | <> | Count how often this function is called. | Sync | <> | Delete a single key from a map. | Sync | <> | Return true (1) if the key exists in this map. Otherwise return false (0). | Sync | <> | Create a log2 histogram of n using buckets per power of 2, 0 <= k <= 5, defaults to 0. | Sync | <> | Return the number of elements in a map. | Sync | <> | Create a linear histogram of n. lhist creates M ((max - min) / step) buckets in the range [min,max) where each bucket is step in size. | Sync | <> | Update the map with n if n is bigger than the current value held. | Sync | <> | Update the map with n if n is smaller than the current value held. | Sync | <> | Combines the count, avg and sum calls into one. | Sync | <> | Calculate the sum of all n passed. | Sync | <> | Set all values for all keys to zero. | Async |=== [#map-functions-avg] === avg .variants * `avg_t avg(int64 n)` Calculate the running average of `n` between consecutive calls. ---- interval:s:1 { @x++; @y = avg(@x); print(@x); print(@y); } ---- Internally this keeps two values in the map: value count and running total. The average is computed in user-space when printing by dividing the total by the count. However, you can get the average in kernel space in expressions like `if (@y == 5)` but this is expensive as bpftrace needs to iterate over all the cpus to collect and sum BOTH count and total. [#map-functions-clear] === clear .variants * `void clear(map m)` *async* Clear all keys/values from map `m`. ---- interval:ms:100 { @[rand % 10] = count(); } interval:s:10 { print(@); clear(@); } ---- [#map-functions-count] === count .variants * `count_t count()` Count how often this function is called. Using `@=count()` is conceptually similar to `@{plus}{plus}`. The difference is that the `count()` function uses a map type optimized for performance and correctness using cheap, thread-safe writes (PER_CPU). However, sync reads can be expensive as bpftrace needs to iterate over all the cpus to collect and sum these values. Note: This differs from "raw" writes (e.g. `@{plus}{plus}`) where multiple writers to a shared location might lose updates, as bpftrace does not generate any atomic instructions for `{plus}{plus}`. Example one: ---- BEGIN { @ = count(); @ = count(); printf("%d\n", (int64)@); // prints 2 exit(); } ---- Example two: ---- interval:ms:100 { @ = count(); } interval:s:10 { // async read print(@); // sync read if (@ > 10) { print(("hello")); } clear(@); } ---- [#map-functions-delete] === delete .variants * `void delete(map m, mapkey k)` * deprecated `void delete(mapkey k)` Delete a single key from a map. For scalar maps (e.g. no explicit keys), the key is omitted and is equivalent to calling `clear`. For map keys that are composed of multiple values (e.g. `@mymap[3, "hello"] = 1` - remember these values are represented as a tuple) the syntax would be: `delete(@mymap, (3, "hello"));` The, now deprecated, API (supported in version <= 0.21.x) of passing map arguments with the key is still supported: e.g. `delete(@mymap[3, "hello"]);`. ``` kprobe:dummy { @scalar = 1; delete(@scalar); // ok @single["hello"] = 1; delete(@single, "hello"); // ok @associative[1,2] = 1; delete(@associative, (1,2)); // ok delete(@associative); // error delete(@associative, 1); // error // deprecated but ok delete(@single["hello"]); delete(@associative[1, 2]); } ``` [#map-functions-has_key] === has_key .variants * `int has_key(map m, mapkey k)` Return true (1) if the key exists in this map. Otherwise return false (0). Error if called with a map that has no keys (aka scalar map). Return value can also be used for scratch variables and map keys/values. ``` kprobe:dummy { @associative[1,2] = 1; if (!has_key(@associative, (1,3))) { // ok print(("bye")); } @scalar = 1; if (has_key(@scalar)) { // error print(("hello")); } $a = has_key(@associative, (1,2)); // ok @b[has_key(@associative, (1,2))] = has_key(@associative, (1,2)); // ok } ``` [#map-functions-hist] === hist .variants * `hist_t hist(int64 n[, int k])` Create a log2 histogram of `n` using $2^k$ buckets per power of 2, 0 <= k <= 5, defaults to 0. ---- kretprobe:vfs_read { @bytes = hist(retval); } ---- Prints: ---- @: [1M, 2M) 3 | | [2M, 4M) 2 | | [4M, 8M) 2 | | [8M, 16M) 6 | | [16M, 32M) 16 | | [32M, 64M) 27 | | [64M, 128M) 48 |@ | [128M, 256M) 98 |@@@ | [256M, 512M) 191 |@@@@@@ | [512M, 1G) 394 |@@@@@@@@@@@@@ | [1G, 2G) 820 |@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ---- [#map-functions-len] === len .variants * `int64 len(map m)` Return the number of elements in the map. [#map-functions-lhist] === lhist .variants * `lhist_t lhist(int64 n, int64 min, int64 max, int64 step)` Create a linear histogram of `n`. `lhist` creates `M` (`(max - min) / step`) buckets in the range `[min,max)` where each bucket is `step` in size. Values in the range `(-inf, min)` and `(max, inf)` get their get their own bucket too, bringing the total amount of buckets created to `M+2`. ---- interval:ms:1 { @ = lhist(rand %10, 0, 10, 1); } interval:s:5 { exit(); } ---- Prints: ---- @: [0, 1) 306 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [1, 2) 284 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [2, 3) 294 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [3, 4) 318 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4, 5) 311 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [5, 6) 362 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [6, 7) 336 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [7, 8) 326 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [8, 9) 328 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [9, 10) 318 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ---- [#map-functions-max] === max .variants * `max_t max(int64 n)` Update the map with `n` if `n` is bigger than the current value held. Similar to `count` this uses a PER_CPU map (thread-safe, fast writes, slow reads). Note: this is different than the typical userspace `max()` in that bpftrace's `max()` only takes a single argument. The logical "other" argument to compare to is the value in the map the "result" is being assigned to. For example, compare the two logically equivalent samples (C++ vs bpftrace): In C++: ---- int x = std::max(3, 33); // x contains 33 ---- In bpftrace: ---- @x = max(3); @x = max(33); // @x contains 33 ---- Also note that bpftrace takes care to handle the unset case. In other words, there is no default value. The first value you pass to `max()` will always be returned. [#map-functions-min] === min .variants * `min_t min(int64 n)` Update the map with `n` if `n` is smaller than the current value held. Similar to `count` this uses a PER_CPU map (thread-safe, fast writes, slow reads). See `max()` above for how this differs from the typical userspace `min()`. [#map-functions-stats] === stats .variants * `stats_t stats(int64 n)` `stats` combines the `count`, `avg` and `sum` calls into one. ---- kprobe:vfs_read { @bytes[comm] = stats(arg2); } ---- ---- @bytes[bash]: count 7, average 1, total 7 @bytes[sleep]: count 5, average 832, total 4160 @bytes[ls]: count 7, average 886, total 6208 @ ---- [#map-functions-sum] === sum .variants * `sum_t sum(int64 n)` Calculate the sum of all `n` passed. Using `@=sum(5)` is conceptually similar to `@+=5`. The difference is that the `sum()` function uses a map type optimized for performance and correctness using cheap, thread-safe writes (PER_CPU). However, sync reads can be expensive as bpftrace needs to iterate over all the cpus to collect and sum these values. Note: This differs from "raw" writes (e.g. `@+=5`) where multiple writers to a shared location might lose updates, as bpftrace does not generate any implicit atomic operations. Example one: ---- BEGIN { @ = sum(5); @ = sum(6); printf("%d\n", (int64)@); // prints 11 clear(@); exit(); } ---- Example two: ---- interval:ms:100 { @ = sum(5); } interval:s:10 { // async read print(@); // sync read if (@ > 10) { print(("hello")); } clear(@); } ---- [#map-functions-zero] === zero .variants * `void zero(map m)` *async* Set all values for all keys to zero. == Configuration - <> - <> === Config Variables Some behavior can only be controlled through config variables, which are listed here. These can be set via the <> directly in a script (before any probes) or via their environment variable equivalent, which is upper case and includes the `BPFTRACE_` prefix e.g. ``stack_mode``'s environment variable would be `BPFTRACE_STACK_MODE`. ==== cache_user_symbols Default: PER_PROGRAM if ASLR disabled or `-c` option given, PER_PID otherwise. - PER_PROGRAM - each program has its own cache. If there are more processes with enabled ASLR for a single program, this might produce incorrect results. - PER_PID - each process has its own cache. This is accurate for processes with ASLR enabled, and enables bpftrace to preload caches for processes running at probe attachment ti me. If there are many processes running, it will consume a lot of a memory. - NONE - caching disabled. This saves the most memory, but at the cost of speed. ==== cpp_demangle Default: 1 C++ symbol demangling in userspace stack traces is enabled by default. This feature can be turned off by setting the value of this environment variable to `0`. ==== lazy_symbolication Default: 0 For user space symbols, symbolicate lazily/on-demand (1) or symbolicate everything ahead of time (0). ==== log_size Default: 1000000 Log size in bytes. ==== max_bpf_progs Default: 1024 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). ==== max_cat_bytes Default: 10000 Maximum bytes read by cat builtin. ==== max_map_keys Default: 4096 This is the maximum number of keys that can be stored in a map. Increasing the value will consume more memory and increase startup times. There are some cases where you will want to, for example: sampling stack traces, recording timestamps for each page, etc. ==== max_probes Default: 1024 This is the maximum number of probes that bpftrace can attach to. Increasing the value will consume more memory, increase startup times, and can incur high performance overhead or even freeze/crash the system. ==== max_strlen Default: 1024 The maximum length (in bytes) for values created by `str()`, `buf()` and `path()`. This limit is necessary because BPF requires the size of all dynamically-read strings (and similar) to be declared up front. This is the size for all strings (and similar) in bpftrace unless specified at the call site. There is no artificial limit on what you can tune this to. But you may be wasting resources (memory and cpu) if you make this too high. ==== max_type_res_iterations Default: 0 Maximum number of levels of nested field accesses for tracepoint args. 0 is unlimited. ==== missing_probes Default: `warn` Controls handling of probes with multiple kprobe or uprobe attach points which cannot be attached to some functions because they do not exist in the kernel or in the traced binary. The possible options are: - `error` - always fail on missing probes - `warn` - print a warning but continue execution - `ignore` - silently ignore missing probes ==== on_stack_limit Default: 32 The maximum size (in bytes) of individual objects that will be stored on the BPF stack. If they are larger than this limit they will be stored in pre-allocated memory. This exists because the BPF stack is limited to 512 bytes and large objects make it more likely that we'll run out of space. bpftrace can store objects that are larger than the `on_stack_limit` in pre-allocated memory to prevent this stack error. However, storing in pre-allocated memory may be less memory efficient. Lower this default number if you are still seeing a stack memory error or increase it if you're worried about memory consumption. ==== perf_rb_pages Default: 64 Number of pages to allocate per CPU perf ring buffer. The value must be a power of 2. If you're getting a lot of dropped events bpftrace may not be processing events in the ring buffer 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. ==== stack_mode Default: bpftrace Output format for ustack and kstack builtins. Available modes/formats: - bpftrace - perf - raw: no symbolication This can be overwritten at the call site. ==== str_trunc_trailer Default: `..` Trailer to add to strings that were truncated. Set to empty string to disable truncation trailers. ==== print_maps_on_exit Default: 1 Controls whether maps are printed on exit. Set to `0` in order to change the default behavior and not automatically print maps at program exit. ==== symbol_source Default: `dwarf` if `bpftrace` is compiled with LLDB, `symbol_table` otherwise Choose how bpftrace will resolve all `uprobe` symbol locations. Available options: - `dwarf` - locate uprobes using DebugInfo, which yields more accurate stack traces (`ustack`). Fall back to the Symbol Table if it can't locate the probe using DebugInfo. - `symbol_table` - don't use DebugInfo and rely on the ELF Symbol Table instead. If the DebugInfo was rewritten by a post-linkage optimisation tool (like BOLT or AutoFDO), it might yield an incorrect address for a probe location. This config can force using the Symbol Table, for when the DebugInfo returns invalid addresses. === Environment Variables These are not available as part of the standard set of <> and can only be set as environment variables. ==== BPFTRACE_BTF Default: None The path to a BTF file. By default, bpftrace searches several locations to find a BTF file. See src/btf.cpp for the details. ==== BPFTRACE_DEBUG_OUTPUT Default: 0 Outputs bpftrace's runtime debug messages to the trace_pipe. This feature can be turned on by setting the value of this environment variable to `1`. ==== BPFTRACE_KERNEL_BUILD Default: `/lib/modules/$(uname -r)` Only used with `BPFTRACE_KERNEL_SOURCE` if it is out-of-tree Linux kernel build. ==== BPFTRACE_KERNEL_SOURCE Default: `/lib/modules/$(uname -r)` bpftrace requires kernel headers for certain features, which are searched for in this directory. ==== BPFTRACE_VMLINUX Default: None This specifies the vmlinux path used for kernel symbol resolution when attaching kprobe to offset. If this value is not given, bpftrace searches vmlinux from pre defined locations. See src/attached_probe.cpp:find_vmlinux() for details. ==== BPFTRACE_COLOR Default: auto Colorize the bpftrace log output message. Valid values are auto, always and never. == Advanced Topics === Address Spaces Kernel and user pointers live in different address spaces which, depending on the CPU architecture, might overlap. Trying to read a pointer that is in the wrong address space results in a runtime error. This error is hidden by default but can be enabled with the `-k` flag: ---- stdin:1:9-12: WARNING: Failed to probe_read_user: Bad address (-14) BEGIN { @=*uptr(kaddr("do_poweroff")) } ~~~ ---- bpftrace tries to automatically set the correct address space for a pointer based on the probe type, but might fail in cases where it is unclear. The address space can be changed with the <> and <> functions. === BTF Support If the kernel version has BTF support, kernel types are automatically available and there is no need to include additional headers to use them. It is not recommended to mix definitions from multiple sources (ie. BTF and header files). If your program mixes definitions, bpftrace will do its best but can easily get confused due to redefinition conflicts. Prefer to exclusively use BTF as it can never get out of sync on a running system. BTF is also less susceptible to parsing failures (C is constantly evolving). Almost all current linux deployments will support BTF. 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 for vmlinux: * Linux 4.18+ with CONFIG_DEBUG_INFO_BTF=y ** Building requires dwarves with pahole v1.13+ * bpftrace v0.9.3+ with BTF support (built with libbpf v0.0.4+) Additional requirements for using BTF for kernel modules: * Linux 5.11+ with CONFIG_DEBUG_INFO_BTF_MODULES=y ** Building requires dwarves with pahole v1.19+ See kernel documentation for more information on BTF. === Clang Environment Variables bpftrace parses header files using libclang, the C interface to Clang. Thus environment variables affecting the clang toolchain can be used. For example, if header files are included from a non-default directory, the `CPATH` or `C_INCLUDE_PATH` environment variables can be set to allow clang to locate the files. See clang documentation for more information on these environment variables and their usage. === Complex Tools bpftrace can be used to create some powerful one-liners and some simple tools. For complex tools, which may involve command line options, positional parameters, argument processing, and customized output, consider switching to bcc. bcc provides Python (and other) front-ends, enabling usage of all the other Python libraries (including argparse), as well as a direct control of the kernel BPF program. The down side is that bcc is much more verbose and laborious to program. Together, bpftrace and bcc are complimentary. An expected development path would be exploration with bpftrace one-liners, then and ad hoc scripting with bpftrace, then finally, when needed, advanced tooling with bcc. As an example of bpftrace vs bcc differences, the bpftrace xfsdist.bt tool also exists in bcc as xfsdist.py. Both measure the same functions and produce the same summary of information. However, the bcc version supports various arguments: ---- # ./xfsdist.py -h usage: xfsdist.py [-h] [-T] [-m] [-p PID] [interval] [count] Summarize XFS operation latency positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -T, --notimestamp don't include timestamp on interval output -m, --milliseconds output in milliseconds -p PID, --pid PID trace this PID only examples: ./xfsdist # show operation latency as a histogram ./xfsdist -p 181 # trace PID 181 only ./xfsdist 1 10 # print 1 second summaries, 10 times ./xfsdist -m 5 # 5s summaries, milliseconds ---- The bcc version is 131 lines of code. The bpftrace version is 22. === Errors . Looks like the BPF stack limit of 512 bytes is exceeded BPF programs that operate on many data items may hit this limit. There are a number of things you can try to stay within the limit: .. Find ways to reduce the size of the data used in the program. Eg, avoid strings if they are unnecessary: use pid instead of comm. Use fewer map keys. .. Split your program over multiple probes. .. Check the status of the BPF stack limit in Linux (it may be increased in the future, maybe as a tuneable). .. (advanced): Run -d and examine the LLVM IR, and look for ways to optimize src/ast/codegen_llvm.cpp. . Kernel headers not found bpftrace requires kernel headers for certain features, which are searched for by default in: `/lib/modules/$(uname -r)`. 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. === Invocation Mode There are three invocation modes for bpftrace built-in functions. [cols="~,~,~"] |=== | Mode | Description | Example functions | Synchronous | The value/effect of the built-in function is determined/handled right away by the bpf program in the kernel space. | `reg(), str(), ntop()` | Asynchronous | The value/effect of the built-in function is determined/handled later by the bpftrace process in the user space. | `printf(), clear(), exit()` | Compile-time | The value of the built-in function is determined before bpf programs are running. | `kaddr(), cgroupid(), offsetof()` |=== While BPF in the kernel can do a lot there are still things that can only be done from user space, like the outputting (printing) of data. The way bpftrace handles this is by sending events from the BPF program which user-space will pick up some time in the future (usually in milliseconds). Operations that happen in the kernel are 'synchronous' ('sync') and those that are handled in user space are 'asynchronous' ('async') The asynchronous behaviour can lead to some unexpected behavior as updates can happen before user space had time to process the event. The following situations may occur: * event loss: when using printf(), the amount of data printed may be less than the actual number of events generated by the kernel during BPF program's execution. * delayed exit: when using the exit() to terminate the program, bpftrace needs to handle the exit signal asynchronously causing the BPF program may continue to run for some additional time. One example is updating a map value in a tight loop: ---- BEGIN { @=0; unroll(10) { print(@); @++; } exit() } ---- Maps are printed by reference not by value and as the value gets updated right after the print user-space will likely only see the final value once it processes the event: ---- @: 10 @: 10 @: 10 @: 10 @: 10 @: 10 @: 10 @: 10 @: 10 @: 10 ---- Therefore, when you need precise event statistics, it is recommended to use synchronous functions (e.g. count() and hist()) to ensure more reliable and accurate results. === Map Printing By default when a bpftrace program exits it will print all maps to stdout. If you don't want this, you can either override the `print_maps_on_exit` configuration option or you can specify an `END` probe and `clear` the maps you don't want printed. For example, these two scripts are equivalent and will print nothing on exit: ``` config = { print_maps_on_exit=0 } BEGIN { @a = 1; @b[1] = 1; } ``` ``` BEGIN { @a = 1; @b[1] = 1; } END { clear(@a); clear(@b); } ``` === Options Expanded ==== Debug Output The `-d STAGE` option produces debug output. It prints the output of the bpftrace execution stage given by the _STAGE_ argument. The option can be used multiple times (with different stage names) and the special value `all` prints the output of all the supported stages. The option also takes multiple stages in one invocation as comma separated values. Note: This is primarily used for bpftrace developers. The supported options are: [cols="~,~"] |=== | `ast` | Prints the Abstract Syntax Tree (AST) after every pass. | `codegen` | Prints the unoptimized LLVM IR as produced by `CodegenLLVM`. | `codegen-opt` | Prints the optimized LLVM IR, i.e. the code which will be compiled into BPF bytecode. | `dis` | Disassembles and prints out the generated bytecode that `libbpf` will see. Only available in debug builds. | `libbpf` | Captures and prints libbpf log for all libbpf operations that bpftrace uses. | `verifier` | Captures and prints the BPF verifier log. | `all` | Prints the output of all of the above stages. |=== ==== Listing Probes Probe listing is the method to discover which probes are supported by the current system. Listing supports the same syntax as normal attachment does and alternatively can be combined with `-e` or filename args to see all the probes that a program would attach to. ---- # bpftrace -l 'kprobe:*' # bpftrace -l 't:syscalls:*openat* # bpftrace -l 'kprobe:tcp*,trace # bpftrace -l 'k:*socket*,tracepoint:syscalls:*tcp*' # bpftrace -l -e 'tracepoint:xdp:mem_* { exit(); }' # bpftrace -l my_script.bt # bpftrace -lv 'enum cpu_usage_stat' ---- The verbose flag (`-v`) can be specified to inspect arguments (`args`) for providers that support it: ---- # bpftrace -l 'fexit:tcp_reset,tracepoint:syscalls:sys_enter_openat' -v fexit:tcp_reset struct sock * sk struct sk_buff * skb tracepoint:syscalls:sys_enter_openat int __syscall_nr int dfd const char * filename int flags umode_t mode # bpftrace -l 'uprobe:/bin/bash:rl_set_prompt' -v # works only if /bin/bash has DWARF uprobe:/bin/bash:rl_set_prompt const char *prompt # bpftrace -lv 'struct css_task_iter' struct css_task_iter { struct cgroup_subsys *ss; unsigned int flags; struct list_head *cset_pos; struct list_head *cset_head; struct list_head *tcset_pos; struct list_head *tcset_head; struct list_head *task_pos; struct list_head *cur_tasks_head; struct css_set *cur_cset; struct css_set *cur_dcset; struct task_struct *cur_task; struct list_head iters_node; }; ---- ==== Preprocessor Options The `-I` option can be used to add directories to the list of directories that bpftrace uses to look for headers. Can be defined multiple times. ---- # cat program.bt #include BEGIN { @ = FOO } # bpftrace program.bt definitions.h:1:10: fatal error: 'foo.h' file not found # /tmp/include foo.h # bpftrace -I /tmp/include program.bt Attaching 1 probe... ---- The `--include` option can be used to include headers by default. Can be defined multiple times. Headers are included in the order they are defined, and they are included before any other include in the program being executed. ---- # bpftrace --include linux/path.h --include linux/dcache.h \ -e 'kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); }' Attaching 1 probe... open path: .com.google.Chrome.ASsbu2 open path: .com.google.Chrome.gimc10 open path: .com.google.Chrome.R1234s ---- ==== Verbose Output The `-v` option prints more information about the program as it is run: ---- # bpftrace -v -e 'tracepoint:syscalls:sys_enter_nanosleep { printf("%s is sleeping.\n", comm); }' AST node count: 7 Attaching 1 probe... load tracepoint:syscalls:sys_enter_nanosleep, with BTF, with func_infos: Success Program ID: 111 Attaching tracepoint:syscalls:sys_enter_nanosleep iscsid is sleeping. iscsid is sleeping. [...] ---- === Systemd support To run bpftrace in the background using systemd:: ---- # systemd-run --unit=bpftrace --service-type=notify bpftrace -e 'kprobe:do_nanosleep { printf("%d sleeping\n", pid); }' ---- In the above example, systemd-run will not finish until bpftrace has attached its probes, so you can be sure that all following commands will be traced. To stop tracing, run `systemctl stop bpftrace`. To debug early boot issues, bpftrace can be invoked via a systemd service ordered before the service that needs to be traced. A basic unit file to run bpftrace before another service looks as follows:: ---- [Unit] Before=service-i-want-to-trace.service [Service] Type=notify ExecStart=bpftrace -e 'kprobe:do_nanosleep { printf("%d sleeping\n", pid); }' ---- Similarly to the systemd-run example, the service to be traced will not start until bpftrace started by the systemd unit has attached its probes. === PER_CPU types For bpftrace PER_CPU types (search this document for "PER_CPU"), you may coerce (and thus force a more expensive synchronous read) the type to an integer using a cast or by doing a comparison. This is useful for when you need an integer during comparisons, `printf()`, or other. For example: ---- BEGIN { @c = count(); @s = sum(3); @s = sum(9); if (@s == 12) { // Coerces @s printf("%d %d\n", (int64)@c, (int64)@s); // Coerces @c and @s and prints "1 12" } } ---- == Terminology [cols="~,~"] |=== | BPF | Berkeley Packet Filter: a kernel technology originally developed for optimizing the processing of packet filters (eg, tcpdump expressions). | BPF map | A BPF memory object, which is used by bpftrace to create many higher-level objects. | BTF | BPF Type Format: the metadata format which encodes the debug info related to BPF program/map. | dynamic tracing | Also known as dynamic instrumentation, this is a technology that can instrument any software event, such as function calls and returns, by live modification of instruction text. Target software usually does not need special capabilities to support dynamic tracing, other than a symbol table that bpftrace can read. Since this instruments all software text, it is not considered a stable API, and the target functions may not be documented outside of their source code. | eBPF | Enhanced BPF: a kernel technology that extends BPF so that it can execute more generic programs on any events, such as the bpftrace programs listed below. It makes use of the BPF sandboxed virtual machine environment. Also note that eBPF is often just referred to as BPF. | kprobes | A Linux kernel technology for providing dynamic tracing of kernel functions. | probe | An instrumentation point in software or hardware, that generates events that can execute bpftrace programs. | static tracing | Hard-coded instrumentation points in code. Since these are fixed, they may be provided as part of a stable API, and documented. | tracepoints | A Linux kernel technology for providing static tracing. | uprobes | A Linux kernel technology for providing dynamic tracing of user-level functions. | USDT | User Statically-Defined Tracing: static tracing points for user-level software. Some applications support USDT. |=== == Supported architectures x86_64, arm64, s390x, arm32, loongarch64, mips64, ppc64, riscv64 == Program Files Programs saved as files are often called scripts and can be executed by specifying their file name. It is convention to use the `.bt` file extension but it is not required. For example, listing the sleepers.bt file using `cat`: ---- # cat sleepers.bt tracepoint:syscalls:sys_enter_nanosleep { printf("%s is sleeping.\n", comm); } ---- And then calling it: ---- # bpftrace sleepers.bt Attaching 1 probe... iscsid is sleeping. iscsid is sleeping. ---- It can also be made executable to run stand-alone. Start by adding an interpreter line at the top (`#!`) with either the path to your installed bpftrace (/usr/local/bin is the default) or the path to `env` (usually just `/usr/bin/env`) followed by `bpftrace` (so it will find bpftrace in your `$PATH`): ---- #!/usr/local/bin/bpftrace tracepoint:syscalls:sys_enter_nanosleep { printf("%s is sleeping.\n", comm); } ---- Then make it executable: ---- # chmod 755 sleepers.bt # ./sleepers.bt Attaching 1 probe... iscsid is sleeping. iscsid is sleeping. ---- bpftrace-0.23.2/man/man8/000077500000000000000000000000001477746507000150235ustar00rootroot00000000000000bpftrace-0.23.2/man/man8/CMakeLists.txt000066400000000000000000000007441477746507000175700ustar00rootroot00000000000000find_program(GZIP gzip REQUIRED) file(GLOB FILES *.8) set(GZFILES "") foreach(FIL ${FILES}) get_filename_component(NAME ${FIL} NAME) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.gz COMMAND ${GZIP} -nc ${FIL} > ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.gz DEPENDS ${FIL}) list(APPEND GZFILES "${CMAKE_CURRENT_BINARY_DIR}/${NAME}.gz") endforeach() add_custom_target(man_man DEPENDS ${GZFILES}) install(FILES ${GZFILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man8) bpftrace-0.23.2/man/man8/bashreadline.bt.8000066400000000000000000000025671477746507000201530ustar00rootroot00000000000000.TH bashreadline.bt 8 "2018-09-06" "USER COMMANDS" .SH NAME bashreadline.bt \- Print bash commands system wide. Uses bpftrace/eBPF. .SH SYNOPSIS .B bashreadline.bt .SH DESCRIPTION bashreadline traces the return of the readline() function using uretprobes, to show the bash commands that were entered interactively, system wide. The entered command may fail: this is just showing what was entered. This program is also a basic example of bpftrace and uretprobes. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace bash commands system wide: # .B bashreadline.bt .SH FIELDS .TP TIME A timestamp on the output, in "HH:MM:SS" format. .TP PID The process ID for bash. .TP COMMAND Entered command. .SH OVERHEAD As the rate of interactive bash commands is expected to be very low (<<100/s), the overhead of this program is expected to be negligible. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO opensnoop.bt(8) bpftrace-0.23.2/man/man8/biolatency.bt.8000066400000000000000000000037171477746507000176610ustar00rootroot00000000000000.TH biolatency.bt 8 "2018-09-13" "USER COMMANDS" .SH NAME biolatency.bt \- Block I/O latency as a histogram. Uses bpftrace/eBPF. .SH SYNOPSIS .B biolatency.bt .SH DESCRIPTION This tool summarizes time (latency) spent in block device I/O (disk I/O) as a power-of-2 histogram. This allows the distribution to be studied, including modes and outliers. There are often two modes, one for device cache hits and one for cache misses, which can be shown by this tool. Latency outliers will also be shown. The original tool, which is retained as "biolatency-kp.bt", currently works by dynamic tracing of the blk_account*() kernel functions, which will need updating to match any changes to these functions in future kernels versions. The updated version of the tool utilizes tracepoints instead of kprobes so that it can be compatible with a wide range of kernel versions. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace block device I/O (disk I/O), and print a latency histogram on Ctrl-C: # .B biolatency.bt .SH FIELDS .TP 1st, 2nd This is a range of latency, in microseconds (shown in "[...)" set notation). .TP 3rd A column showing the count of operations in this range. .TP 4th This is an ASCII histogram representing the count column. .SH OVERHEAD Since block device I/O usually has a relatively low frequency (< 10,000/s), the overhead for this tool is expected to be negligible. For high IOPS storage systems, test and quantify before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO biosnoop.bt(8) bpftrace-0.23.2/man/man8/biosnoop.bt.8000066400000000000000000000034331477746507000173530ustar00rootroot00000000000000.TH biosnoop.bt 8 "2018-09-11" "USER COMMANDS" .SH NAME biosnoop.bt \- Block I/O tracing tool, showing per I/O latency. Uses bpftrace/eBPF. .SH SYNOPSIS .B biosnoop.bt .SH DESCRIPTION This is a basic block I/O (disk I/O) tracing tool, showing each I/O event along with the issuing process ID, and the I/O latency. This can be used to investigate disk I/O performance issues. This tool currently works by dynamic tracing of the blk_account*() kernel functions, which will need updating to match any changes to these functions in future kernels versions. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace block I/O events, printing per-line summaries: # .B biosnoop.bt .SH FIELDS .TP TIME Time of the I/O completion, in milliseconds since program start. .TP COMM Issuing process name. This often identifies the issuing application process, but I/O may be initiated from kernel threads only. .TP PID Issuing process ID. This often identifies the issuing application process, but I/O may be initiated from kernel threads only. .TP ARGS Process name and arguments (16 word maximum). .SH OVERHEAD Since block device I/O usually has a relatively low frequency (< 10,000/s), the overhead for this tool is expected to be negligible. For high IOPS storage systems, test and quantify before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool provides more fields. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO opensnoop.bt(8) bpftrace-0.23.2/man/man8/biostacks.bt.8000066400000000000000000000032401477746507000175010ustar00rootroot00000000000000.TH biostacks.bt 8 "2019-07-12" "USER COMMANDS" .SH NAME biostacks \- Show disk I/O latency with initialization stacks. Uses bpftrace/eBPF. .SH SYNOPSIS .B biostacks .SH DESCRIPTION This tool shows disk I/O latency histograms for each block I/O initialization path. This can help reveal the reason for different latencies, as some may be created by log flushing, others by application reads, etc. This works by attaching to block_io_start and block_rq_issue tracepoints. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace disk I/O latency with initialization stacks: # .B biostacks.bt .SH FIELDS .TP 0th An initialization kernel stack trace (shown in "@[...]") is printed before each I/O histogram. .TP 1st, 2nd This is a range of I/O latency, in microseconds (shown in "[...)" set notation). .TP 3rd A column showing the count of I/O in this range. .TP 4th This is an ASCII histogram representing the count column. .SH OVERHEAD The rate of biostacks should be low (bounded by device IOPS), such that the overhead of this tool is expected to be negligible. .SH SOURCE This tool originated from the book "BPF Performance Tools", published by Addison Wesley (2019): .IP http://www.brendangregg.com/bpf-performance-tools-book.html .PP See the book for more documentation on this tool. .PP This version is in the bpftrace repository: .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO biosnoop.bt(8) bpftrace-0.23.2/man/man8/bitesize.bt.8000066400000000000000000000030061477746507000173350ustar00rootroot00000000000000.TH bitesize.bt 8 "2018-09-07" "USER COMMANDS" .SH NAME bitesize.bt \- Show disk I/O size as a histogram. Uses bpftrace/eBPF. .SH SYNOPSIS .B bitesize.bt .SH DESCRIPTION This can be used to characterize the distribution of block device (disk) I/O sizes. To study block device I/O in more detail, see biosnoop.bt(8). This uses the tracepoint:block:block_rq_issue tracepoint, and is a simple example of bpftrace. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace block I/O and summarize as a histogram by process: # .B bitesize.bt .SH FIELDS .TP 0th A process name (shown in "@[...]") is printed before each I/O histogram. .TP 1st, 2nd This is a range of I/O sizes, in Kbytes (shown in "[...)" set notation). .TP 3rd A column showing the count of I/O in this range. .TP 4th This is an ASCII histogram representing the count column. .SH OVERHEAD Since block device I/O usually has a relatively low frequency (< 10,000/s), the overhead for this tool is expected to be low or negligible. For high IOPS storage systems, test and quantify before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO biosnoop.bt(8) bpftrace-0.23.2/man/man8/capable.bt.8000066400000000000000000000027041477746507000171120ustar00rootroot00000000000000.TH capable.bt 8 "2018-09-08" "USER COMMANDS" .SH NAME capable.bt \- Trace security capability checks (cap_capable()). .SH SYNOPSIS .B capable.bt .SH DESCRIPTION This traces security capability checks in the kernel, and prints details for each call. This can be useful for general debugging, and also security enforcement: determining a white list of capabilities an application needs. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF, bpftrace. .SH EXAMPLES .TP Trace all capability checks system-wide: # .B capable.bt .SH FIELDS .TP TIME(s) Time of capability check: HH:MM:SS. .TP UID User ID. .TP PID Process ID. .TP COMM Process name. CAP Capability number. NAME Capability name. See capabilities(7) for descriptions. .TP AUDIT Whether this was an audit event. .SH OVERHEAD This adds low-overhead instrumentation to capability checks, which are expected to be low frequency, however, that depends on the application. Test in a lab environment before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool provides options to customize the output. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO capabilities(7) bpftrace-0.23.2/man/man8/cpuwalk.bt.8000066400000000000000000000023141477746507000171660ustar00rootroot00000000000000.TH cpuwalk.bt 8 "2018-09-08" "USER COMMANDS" .SH NAME cpuwalk.bt \- Sample which CPUs are executing processes.. Uses bpftrace/eBPF. .SH SYNOPSIS .B cpuwalk.bt .SH DESCRIPTION This tool samples CPUs at 99 Hertz, then prints a histogram showing which CPUs were active. 99 Hertz is used to avoid lockstep sampling that would skew results. This tool can help identify if your application's workload is evenly using the CPUs, or if there is an imbalance problem. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Sample CPUs and print a summary on Ctrl-C: # .B cpuwalk.bt .SH FIELDS .TP 1st, 2nd The CPU is shown in the first field, after the "[". Disregard the second field. .TP 3rd A column showing the number of samples for this CPU. .TP 4th This is an ASCII histogram representing the count column. .SH OVERHEAD This should be negligible. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO mpstat(1) bpftrace-0.23.2/man/man8/dcsnoop.bt.8000066400000000000000000000040211477746507000171620ustar00rootroot00000000000000.TH dcsnoop.bt 8 "2018-09-08" "USER COMMANDS" .SH NAME dcsnoop.bt \- Trace directory entry cache (dcache) lookups. Uses bpftrace/eBPF. .SH SYNOPSIS .B dcsnoop.bt .SH DESCRIPTION By default, this traces every dcache lookup, and shows the process performing the lookup and the filename requested. The output of this tool can be verbose, and is intended for further investigations of dcache performance beyond dcstat(8), which prints per-second summaries. This uses kernel dynamic tracing of the d_lookup() function, and will need and will need updating to match any changes to this function. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Trace all dcache lookups: # .B dcsnoop.bt .SH FIELDS .TP TIME(ms) Time of lookup, in milliseconds. .TP PID Process ID. .TP COMM Process name. .TP T Type: R == reference, M == miss. A miss will print two lines, one for the reference, and one for the miss. .TP FILE The file name component that was being looked up. This contains trailing pathname components (after '/'), which will be the subject of subsequent lookups. .SH OVERHEAD File name lookups can be frequent (depending on the workload), and this tool prints a line for each failed lookup, and with \-a, each reference as well. The output may be verbose, and the incurred overhead, while optimized to some extent, may still be from noticeable to significant. This is only really intended for deeper investigations beyond dcstat(8), when absolutely necessary. Measure and quantify the overhead in a test environment before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO dcstat(8) bpftrace-0.23.2/man/man8/execsnoop.bt.8000066400000000000000000000035301477746507000175240ustar00rootroot00000000000000.TH execsnoop.bt 8 "2018-09-11" "USER COMMANDS" .SH NAME execsnoop.bt \- Trace new processes via exec() syscalls. Uses bpftrace/eBPF. .SH SYNOPSIS .B execsnoop.bt .SH DESCRIPTION This traces when processes call exec() (execve()). It is handy for identifying new processes created via the usual fork()->exec() sequence. Note that the return value is not currently traced, so the exec() may have failed. This tool is useful for debugging shell scripts, including application startup. It is also useful for identifying a type of performance issue: a flood of short-lived processes, that end quickly and aren't readily visible in top(1). Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace all new processes calling execve(): # .B execsnoop.bt .SH FIELDS .TP TIME Time of the exec() call, in milliseconds since program start. .TP PID Process ID .TP PPID Parent Process ID .TP ARGS Process name and arguments (16 word maximum). .SH OVERHEAD This traces the execve() tracepoint and prints output for each event. As the rate of this is generally expected to be low (< 100/s), the overhead is also expected to be negligible. If you have an application that is spawning a high rate of new processes for a reason (large build process), this could cause a small amount of overhead: test and understand overhead before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool provides more fields and options to customize the output. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO opensnoop.bt(8) bpftrace-0.23.2/man/man8/gethostlatency.bt.8000066400000000000000000000033401477746507000205550ustar00rootroot00000000000000.TH gethostlatency.bt 8 "2018-09-08" "USER COMMANDS" .SH NAME gethostlatency.bt \- Show latency for getaddrinfo/gethostbyname[2] calls. Uses bpftrace/eBPF. .SH SYNOPSIS .B gethostlatency.bt .SH DESCRIPTION This traces and prints when getaddrinfo(), gethostbyname(), and gethostbyname2() are called, system wide, and shows the responsible PID and command name, latency of the call (duration) in milliseconds, and the host string. This tool can be useful for identifying DNS latency, by identifying which remote host name lookups were slow, and by how much. This tool currently uses dynamic tracing of user-level functions and registers, and may need modifications to match your software and processor architecture. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Trace host lookups (getaddrinfo/gethostbyname[2]) system wide: # .B gethostlatency.bt .SH FIELDS .TP TIME Time of the command (HH:MM:SS). .TP PID Process ID of the client performing the call. .TP COMM Process (command) name of the client performing the call. .TP LATms Latency of the call, in milliseconds. .TP HOST Host name string: the target of the lookup. .SH OVERHEAD The rate of lookups should be relatively low, so the overhead is not expected to be a problem. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool provides command line options. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO tcpdump(8) bpftrace-0.23.2/man/man8/killsnoop.bt.8000066400000000000000000000031411477746507000175310ustar00rootroot00000000000000.TH killsnoop.bt 8 "2018-09-07" "USER COMMANDS" .SH NAME killsnoop.bt \- Trace signals issued by the kill() syscall. Uses bpftrace/eBPF. .SH SYNOPSIS .B killsnoop.bt .SH DESCRIPTION killsnoop traces the kill() syscall, to show signals sent via this method. This may be useful to troubleshoot failing applications, where an unknown mechanism is sending signals. This works by tracing the kill() syscall tracepoints. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace all kill() syscalls: # .B killsnoop.bt .SH FIELDS .TP TIME Time of the kill call. .TP PID Source process ID .TP COMM Source process name .TP SIG Signal number. See signal(7). .TP TPID Target process ID .TP RES Result. 0 == success, a negative value (of the error code) for failure. .SH OVERHEAD This traces the kernel kill function and prints output for each event. As the rate of this is generally expected to be low (< 100/s), the overhead is also expected to be negligible. If you have an application that is calling a very high rate of kill()s for some reason, then test and understand overhead before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO opensnoop.bt(8) bpftrace-0.23.2/man/man8/loads.bt.8000066400000000000000000000037401477746507000166260ustar00rootroot00000000000000.TH loads.bt 8 "2018-09-10" "USER COMMANDS" .SH NAME loads.bt \- Prints load averages. Uses bpftrace/eBPF. .SH SYNOPSIS .B loads.bt .SH DESCRIPTION These are the same load averages printed by "uptime", but to three decimal places instead of two (not that it really matters). This is really a demonstration of fetching and processing a kernel structure from bpftrace. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Print system load averages every second: # .B loads.bt .SH FIELDS .TP HH:MM:SS Each output line includes time of printing in "HH:MM:SS" format. .TP load averages: These are exponentially-damped moving sum averages of the system loads. Load is a measurement of demand on system resources, which include CPUs and other resources that are accessed with the kernel in an uninterruptible state (TASK_UNINTERRUPTIBLE), which includes types of disk I/O and lock accesses. Linux load averages originally reflected CPU demand only, as it does in other OSes, but this was changed in Linux 0.99.14. This demand measurement reflects not just the utilized resource, but also the queued demand (a saturation measurement). Finally, the three numbers are called the "one-", "five-", and "fifteen-minute" load averages, however these times are constants used in the exponentially-damping equation, and the load averages reflect load beyond these times. Were you expecting an accurate description of load averages in the man page of a bpftrace tool? .SH OVERHEAD Other than bpftrace startup time, negligible. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH REFERENCE For more on load averages, see: .PP http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO uptime(1) bpftrace-0.23.2/man/man8/mdflush.bt.8000066400000000000000000000032221477746507000171610ustar00rootroot00000000000000.TH mdflush.bt 8 "2018-09-07" "USER COMMANDS" .SH NAME mdflush.bt \- Trace md flush events. Uses bpftrace/eBPF. .SH SYNOPSIS .B mdflush.bt .SH DESCRIPTION This tool traces flush events by md, the Linux multiple device driver (software RAID). The timestamp and md device for the flush are printed. Knowing when these flushes happen can be useful for correlation with unexplained spikes in disk latency. This works by tracing the kernel md_flush_request() function using kernel dynamic tracing, and will need updating to match any changes to this function. Note that the flushes themselves are likely to originate from higher in the I/O stack, such as from the file systems. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace md flush events: # .B mdflush.bt .SH FIELDS .TP TIME Time of the flush event (HH:MM:SS). .TP PID The process ID that was on-CPU when the event was issued. This may identify the cause of the flush (eg, the "sync" command), but will often identify a kernel worker thread that was managing I/O. .TP COMM The command name for the PID. .TP DEVICE The md device name. .SH OVERHEAD Expected to be negligible. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO biosnoop.bt(8) bpftrace-0.23.2/man/man8/naptime.bt.8000066400000000000000000000030021477746507000171500ustar00rootroot00000000000000.TH naptime.bt 8 "2019-07-05" "USER COMMANDS" .SH NAME naptime.bt \- Trace voluntary sleep calls. Uses bpftrace/eBPF. .SH SYNOPSIS .B naptime.bt .SH DESCRIPTION This tool traces application sleeps, and can be used for debugging high latency that may be caused by deliberate sleeps placed in application routines, especially administration scripts. This tool works by tracing the nanosleep(2) syscall using the syscall tracepoints. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace application sleeps via nanosleep(2): # .B naptime.bt .SH FIELDS .TP TIME A timestamp in HH:MM:SS format. .TP PPID Parent process ID. .TP PCOMM Parent process name. .TP PID The sleeping process ID. .TP COMM The sleeping process name. .TP SECONDS The requested duration of the sleep. .SH OVERHEAD nanosleep(2) calls are expected to be low frequency (<< 100/s), so the overhead of this tool is expected to be negligible. .SH SOURCE This tool originated from the book "BPF Performance Tools", published by Addison Wesley (2019): .IP http://www.brendangregg.com/bpf-performance-tools-book.html .PP See the book for more documentation on this tool. .PP This version is in the bpftrace repository: .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO capable.bt(8) bpftrace-0.23.2/man/man8/oomkill.bt.8000066400000000000000000000034201477746507000171650ustar00rootroot00000000000000.TH oomkill.bt 8 "2018-09-07" "USER COMMANDS" .SH NAME oomkill.bt \- Trace OOM killer. Uses bpftrace/eBPF. .SH SYNOPSIS .B oomkill.bt .SH DESCRIPTION This traces the kernel out-of-memory killer, and prints basic details, including the system load averages at the time of the OOM kill. This can provide more context on the system state at the time: was it getting busier or steady, based on the load averages? This tool may also be useful to customize for investigations; for example, by adding other task_struct details at the time of OOM, or by adding other commands to run at the shell. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace OOM kill events: # .B oomkill.bt .SH FIELDS .TP Triggered by ... The process ID and process name of the task that was running when another task was OOM killed. .TP OOM kill of ... The process ID and name of the target process that was OOM killed. .TP loadavg Contents of /proc/loadavg. The first three numbers are 1, 5, and 15 minute load averages (where the average is an exponentially damped moving sum, and those numbers are constants in the equation); then there is the number of running tasks, a slash, and the total number of tasks; and then the last number is the last PID to be created. .SH OVERHEAD Negligible. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO dmesg(1) bpftrace-0.23.2/man/man8/opensnoop.bt.8000066400000000000000000000031121477746507000175350ustar00rootroot00000000000000.TH opensnoop.bt 8 "2018-09-08" "USER COMMANDS" .SH NAME opensnoop.bt \- Trace open() syscalls. Uses bpftrace/eBPF. .SH SYNOPSIS .B opensnoop.bt .SH DESCRIPTION opensnoop traces the open() syscall, showing which processes are attempting to open which files. This can be useful for determining the location of config and log files, or for troubleshooting applications that are failing, specially on startup. This works by tracing the open() syscall tracepoint. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Trace all open() syscalls: # .B opensnoop.bt .SH FIELDS PID Process ID .TP TID Thread ID .TP COMM Process name .TP FD File descriptor (if success), or -1 (if failed) .TP ERR Error number (see the system's errno.h) .TP PATH Open path .SH OVERHEAD This traces the open tracepoint and prints output for each event. As the rate of this is generally expected to be low (< 1000/s), the overhead is also expected to be negligible. If you have an application that is calling a high rate of open()s, then test and understand overhead before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO statsnoop.bt(8), execsnoop.bt(8) bpftrace-0.23.2/man/man8/pidpersec.bt.8000066400000000000000000000026611477746507000175030ustar00rootroot00000000000000.TH pidpersec.bt 8 "2018-09-06" "USER COMMANDS" .SH NAME pidpersec.bt \- Count new processes (via fork()). Uses bpftrace/eBPF. .SH SYNOPSIS .B pidpersec.bt .SH DESCRIPTION pidpersec shows how many new processes were created each second. There can be performance issues caused by many short-lived processes, which may not be visible in sampling tools like top(1). pidpersec provides one way to investigate this behavior. This works by tracing the tracepoint:sched:sched_process_fork tracepoint. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Count new processes, printing per-second summaries until Ctrl-C is hit: # .B pidpersec.bt .SH FIELDS .TP 1st Count of processes (after "@") .SH OVERHEAD This traces kernel forks, and maintains an in-kernel count which is read asynchronously from user-space. As the rate of this is generally expected to be low (<< 1000/s), the overhead is also expected to be negligible. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO top(1) bpftrace-0.23.2/man/man8/runqlat.bt.8000066400000000000000000000034321477746507000172100ustar00rootroot00000000000000.TH runqlat.bt 8 "2018-09-17" "USER COMMANDS" .SH NAME runqlat.bt \- CPU scheduler run queue latency as a histogram. Uses bpftrace/eBPF. .SH SYNOPSIS .B runqlat.bt .SH DESCRIPTION This traces time spent waiting in the CPU scheduler for a turn on-CPU. This metric is often called run queue latency, or scheduler latency. This tool shows this latency as a power-of-2 histogram in nanoseconds, allowing multimodal distributions to be studied, as well as latency outliers. This tool uses the sched tracepoints. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace CPU run queue latency system wide, printing a histogram on Ctrl-C: # .B runqlat.bt .SH FIELDS .TP 1st, 2nd This is a range of latency, in microseconds (shown in "[...)" set notation). .TP 3rd A column showing the count of scheduler events in this range. .TP 4th This is an ASCII histogram representing the count column. .SH OVERHEAD This traces scheduler functions, which can become very frequent. While eBPF has very low overhead, and this tool uses in-kernel maps for efficiency, the frequency of scheduler events for some workloads may be high enough that the overhead of this tool becomes significant. Measure in a lab environment to quantify the overhead before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO runqlen.bt(8), mpstat(1), pidstat(1), uptime(1) bpftrace-0.23.2/man/man8/runqlen.bt.8000066400000000000000000000031021477746507000172000ustar00rootroot00000000000000.TH runqlen.bt 8 "2018-10-07" "USER COMMANDS" .SH NAME runqlen.bt \- CPU scheduler run queue length as a histogram. Uses bpftrace/eBPF. .SH SYNOPSIS .B runqlen.bt .SH DESCRIPTION This program summarizes scheduler queue length as a histogram, and can also show run queue occupancy. It works by sampling the run queue length on all CPUs at 99 Hertz. This tool can be used to identify imbalances, eg, when processes are bound to CPUs causing queueing, or interrupt mappings causing the same. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace CPU run queue length system wide, printing a histogram on Ctrl-C: # .B runqlen.bt .SH FIELDS .TP 1st, 2nd The run queue length is shown in the first field (after "["). .TP 3rd A column showing the count of samples in for that length. .TP 4th This is an ASCII histogram representing the count column. .SH OVERHEAD This samples scheduler structs at 99 Hertz across all CPUs. Relatively, this is a low rate of events, and the overhead of this tool is expected to be near zero. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO runqlat.bt(8), mpstat(1), pidstat(1), uptime(1) bpftrace-0.23.2/man/man8/setuids.bt.8000066400000000000000000000030031477746507000171740ustar00rootroot00000000000000.TH setuids.bt 8 "2019-07-05" "USER COMMANDS" .SH NAME setuids.bt \- Trace setuid family of syscalls. Uses bpftrace/eBPF. .SH SYNOPSIS .B setuids.bt .SH DESCRIPTION This tool traces privilege escalation via setuid syscalls, and can be used for debugging, whitelist creation, and intrusion detection. It works by tracing the setuid(2), setfsuid(2), and retresuid(2) syscalls using the syscall tracepoints. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace setuid syscalls: # .B setuids.bt .SH FIELDS .TP PID The calling process ID. .TP COMM The calling process (thread) name. .TP UID The UID of the caller. .TP SYSCALL The syscall name. .TP ARGS The arguments to the syscall .TP (RET) The return value for the syscall: 0 == success, other numbers indicate an error code. .SH OVERHEAD setuid calls are expected to be low frequency (<< 100/s), so the overhead of this tool is expected to be negligible. .SH SOURCE This tool originated from the book "BPF Performance Tools", published by Addison Wesley (2019): .IP http://www.brendangregg.com/bpf-performance-tools-book.html .PP See the book for more documentation on this tool. .PP This version is in the bpftrace repository: .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO capable.bt(8) bpftrace-0.23.2/man/man8/ssllatency.bt.8000066400000000000000000000037321477746507000177060ustar00rootroot00000000000000.TH ssllatency.bt 8 "2021-12-28" "USER COMMANDS" .SH NAME ssllatency.bt \- Show SSL/TLS handshake latency histogram. Uses bpftrace/eBPF. .SH SYNOPSIS .B ssllatency.bt .SH DESCRIPTION ssllatency shows latency distribution for OpenSSL handshake functions. This is useful for performance analysis with different crypto cipher suite, async SSL acceleration by CPU or offload device, etc. This tool works by dynamic tracing the uprobes in OpenSSL and related crypto libs, and may need updating to match future changes to these functions. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace SSL/TLS handshake latency, and print a histogram on Ctrl-C: # .B ssllatency.bt .SH FIELDS .TP 0th A function name is shown in "@hist[...]" followed by latency histogram and "@stat[...]" followed by total call count, average, total latency in microseconds. Non-zero failed calls are traced separately (in "@histF[]" and "@statF[]") for some functions. .TP 1st, 2nd This is a range of latency, in microseconds (shown in "[...)" set notation). .TP 3rd A column showing the count of operations in this range. .TP 4th This is an ASCII histogram representing the count column. .SH OVERHEAD SSL/TLS handshake usually contains network latency and the traced crypto functions are CPU intensive tasks, so call frequency should be low and the overhead of this tool is expected to be negligible. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. There is a bcc tool sslsniff that can show SSL/TLS handshake event latency before sniffing the plaintext in SSL_read/write. This tool provides more detailed crypto latency distribution during the handshake event. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Tao Xu .SH SEE ALSO sslsnoop.bt(8) bpftrace-0.23.2/man/man8/sslsnoop.bt.8000066400000000000000000000031771477746507000174100ustar00rootroot00000000000000.TH sslsnoop.bt 8 "2021-12-28" "USER COMMANDS" .SH NAME sslsnoop.bt \- Show SSL/TLS handshake events. Uses bpftrace/eBPF. .SH SYNOPSIS .B sslsnoop.bt .SH DESCRIPTION sslsnoop traces OpenSSL handshake functions, and shows latency and return value. This can be used to analyze SSL/TLS performance. This tool works by dynamic tracing the uprobes in OpenSSL and related crypto libs, and may need updating to match future changes to these functions. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace SSL/TLS handshake events, printing per-line summaries: # .B sslsnoop.bt .SH FIELDS .TP TIME(us) Time of the call completion, in microseconds since program start. .TP TID Thread ID. .TP COMM Process name. .TP LAT(us) Latency of the call, in microseconds. .TP RET Return value of the call. .TP FUNC Function name. .SH OVERHEAD SSL/TLS handshake usually contains network latency and the traced crypto functions are CPU intensive tasks, so call frequency should be low and the overhead of this tool is expected to be negligible. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. There is a bcc tool sslsniff that can show SSL/TLS handshake event latency before sniffing the plaintext in SSL_read/write. This tool provides more detailed crypto latency distribution during the handshake event. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Tao Xu .SH SEE ALSO biosnoop.bt(8) bpftrace-0.23.2/man/man8/statsnoop.bt.8000066400000000000000000000033151477746507000175540ustar00rootroot00000000000000.TH statsnoop.bt 8 "2018-09-08" "USER COMMANDS" .SH NAME statsnoop.bt \- Trace stat() syscalls. Uses bpftrace/eBPF. .SH SYNOPSIS .B statsnoop.bt .SH DESCRIPTION statsnoop traces the stat() syscall, showing which processes are attempting to stat which files. This can be useful for determining the location of config and log files, or for troubleshooting applications that are failing, specially on startup. This traces the tracepoints for statfs(), statx(), newstat(), and newlstat(). These aren't the only the stat syscalls: if you are missing activity, you may need to add more variants. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Trace all stat() syscalls: # .B statsnoop.bt .SH FIELDS PID Process ID .TP TID Thread ID .TP COMM Process name .TP FD File descriptor (if success), or -1 (if failed) .TP ERR Error number (see the system's errno.h) .TP PATH Stat path .SH OVERHEAD This traces the stat tracepoints and prints output for each event. As the rate of this is generally expected to be low (< 1000/s), the overhead is also expected to be negligible. If you have an application that is calling a high rate of stat()s, then test and understand overhead before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO opensnoop.bt(8), execsnoop.bt(8) bpftrace-0.23.2/man/man8/swapin.bt.8000066400000000000000000000030511477746507000170200ustar00rootroot00000000000000.TH swapin.bt 8 "2019-07-05" "USER COMMANDS" .SH NAME swapin.bt \- Count swapins by process. Uses bpftrace/eBPF. .SH SYNOPSIS .B swapin .SH DESCRIPTION This tool counts swapins by process, to show which process is affected by swapping (if swap devices are in use). This can explain a significant source of application latency, if it has began swapping due to memory pressure on the system. This works by tracing the swap_readpage() kernel function using dynamic instrumentation. This tool may need maintenance to keep working if that function changes in later kernels. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Count swapins by process, showing per-second summaries. # .B swapin.bt .SH FIELDS .TP 1st The process name. .TP 2nd The process ID. .TP 3rd The count of swapins during that interval. .SH OVERHEAD The rate of swapins should be low (bounded by swapin device IOPS), such that the overhead of this tool is expected to be negligible. .SH SOURCE This tool originated from the book "BPF Performance Tools", published by Addison Wesley (2019): .IP http://www.brendangregg.com/bpf-performance-tools-book.html .PP See the book for more documentation on this tool. .PP This version is in the bpftrace repository: .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO swapon(8) bpftrace-0.23.2/man/man8/syncsnoop.bt.8000066400000000000000000000030571477746507000175600ustar00rootroot00000000000000.TH syncsnoop.bt 8 "2018-09-06" "USER COMMANDS" .SH NAME syncsnoop.bt \- Trace the sync() variety of syscalls. Uses bpftrace/eBPF. .SH SYNOPSIS .B syncsnoop.bt .SH DESCRIPTION syncsnoop traces calls to sync() syscalls (sync(), fsync(), msync(), etc), which flushes file system cache and buffers to storage devices. These calls can cause performance perturbations, and it can be useful to know if they are happening, when they happen, and how frequently. This works by tracing the sync() variety of syscalls via tracepoints. This program is also a basic example of eBPF/bcc. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace calls to sync() syscalls: # .B syncsnoop.bt .SH FIELDS .TP TIME A timestamp on the output, in "HH:MM:SS" format. .TP PID The process ID that was on-CPU during the event. .TP COMM The process name that was on-CPU during the event. .TP EVENT The tracepoint name for the sync event. .SH OVERHEAD This traces sync syscalls and prints output for each event. As the rate of this is generally expected to be low (<< 100/s), the overhead is also expected to be negligible. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO iostat(1) bpftrace-0.23.2/man/man8/syscount.bt.8000066400000000000000000000044521477746507000174140ustar00rootroot00000000000000.TH syscount.bt 8 "2018-09-06" "USER COMMANDS" .SH NAME syscount.bt \- Count system calls. Uses bpftrace/eBPF. .SH SYNOPSIS .B syscount.bt .SH DESCRIPTION This counts system calls (syscalls), printing a summary of the top ten syscall IDs, and the top ten process names making syscalls. This can be helpful for characterizing the kernel and resource workload, and finding applications who are using syscalls inefficiently. This works by using the tracepoint:raw_syscalls:sys_enter tracepoint. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Count system calls until Ctrl-C is hit: # .B syscount.bt .SH OUTPUT .TP Top 10 syscalls IDs: This shows the syscall ID number (in @syscall[]) followed by a count for this syscall during tracing. To see the syscall name for that ID, you can use "ausyscall --dump", or the bcc version of this tool that does translations. .TP Top 10 processes: This shows the process name (in @process[]) followed by a count of syscalls during tracing. .SH OVERHEAD For most applications, the overhead should be manageable if they perform 1000's or even 10,000's of syscalls per second. For higher rates, the overhead may become considerable. For example, tracing a microbenchmark loop of 4 million calls to geteuid(), slows it down by 2.4x. However, this represents tracing a workload that has a syscall rate of over 4 million syscalls per second per CPU, which should not be typical (in one large cloud production environment, rates of between 10k and 50k are typical, where the application overhead is expected to be closer to 1%). For comparison, strace(1) in its current ptrace-based implementation (which it has had for decades) runs the same geteuid() workload 102x slower (that's one hundred and two times slower). .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc version provides different command line options, and translates the syscall IDs to their syscall names. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO strace(1) bpftrace-0.23.2/man/man8/tcpaccept.bt.8000066400000000000000000000041511477746507000174670ustar00rootroot00000000000000.TH tcpaccept.bt 8 "2018-10-24" "USER COMMANDS" .SH NAME tcpaccept.bt \- Trace TCP passive connections (accept()). Uses bpftrace/eBPF .SH SYNOPSIS .B tcpaccept.bt .SH DESCRIPTION This tool traces passive TCP connections (eg, via an accept() syscall; connect() are active connections). This can be useful for general troubleshooting to see what new connections the local server is accepting. This uses dynamic tracing of the kernel inet_csk_accept() socket function (from tcp_prot.accept), and will need to be modified to match kernel changes. This tool only traces successful TCP accept()s. Connection attempts to closed ports will not be shown (those can be traced via other functions). Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace all passive TCP connections (accept()s): # .B tcpaccept.bt .TP .SH FIELDS .TP TIME(s) Time of the call, in HH:MM:SS format. .TP PID Process ID .TP COMM Process name .TP RADDR Remote IP address. .TP RPORT Remote port. .TP LADDR Local IP address. .TP LPORT Local port .TP BL Current accept backlog vs maximum backlog .SH OVERHEAD This traces the kernel inet_csk_accept function and prints output for each event. The rate of this depends on your server application. If it is a web or proxy server accepting many tens of thousands of connections per second, then the overhead of this tool may be measurable (although, still a lot better than tracing every packet). If it is less than a thousand a second, then the overhead is expected to be negligible. Test and understand this overhead before use. .SH SOURCE This is from bpftrace .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg, adapted for bpftrace by Dale Hamel .SH SEE ALSO tcpconnect.bt(8), funccount(8), tcpdump(8) bpftrace-0.23.2/man/man8/tcpconnect.bt.8000066400000000000000000000036041477746507000176630ustar00rootroot00000000000000.TH tcpconnect.bt 8 "2018-11-24" "USER COMMANDS" .SH NAME tcpconnect.bt \- Trace TCP active connections (connect()). Uses Linux bpftrace/eBPF .SH SYNOPSIS .B tcpconnect.bt .SH DESCRIPTION This tool traces active TCP connections (eg, via a connect() syscall; accept() are passive connections). This can be useful for general troubleshooting to see what connections are initiated by the local server. All connection attempts are traced, even if they ultimately fail. This works by tracing the kernel tcp_v4_connect() and tcp_v6_connect() functions using dynamic tracing, and will need updating to match any changes to these functions. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace all active TCP connections: # .B tcpconnect.bt .TP .SH FIELDS .TP TIME(s) Time of the call, in HH:MM:SS format. .TP PID Process ID .TP COMM Process name .TP SADDR Source IP address. .TP SPORT Source port. .TP DADDR Destination IP address. .TP DPORT Destination port .SH OVERHEAD This traces the kernel tcp_v[46]_connect functions and prints output for each event. As the rate of this is generally expected to be low (< 1000/s), the overhead is also expected to be negligible. If you have an application that is calling a high rate of connects()s, such as a proxy server, then test and understand this overhead before use. .SH SOURCE This is from bpftrace .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg, adapted for bpftrace by Dale Hamel .SH SEE ALSO tcpaccept.bt(8), funccount(8), tcpdump(8) bpftrace-0.23.2/man/man8/tcpdrop.bt.8000066400000000000000000000041031477746507000171710ustar00rootroot00000000000000.TH tcpdrop.bt 8 "2018-11-24" "USER COMMANDS" .SH NAME tcpdrop.bt \- Trace kernel-based TCP packet drops with details. Uses Linux bpftrace/eBPF .SH SYNOPSIS .B tcpdrop.bt .SH DESCRIPTION This tool traces TCP packets or segments that were dropped by the kernel, and shows details from the IP and TCP headers, the socket state, and the kernel stack trace. This is useful for debugging cases of high kernel drops, which can cause timer-based retransmits and performance issues. This tool works using dynamic tracing of the tcp_drop() kernel function, which requires a recent kernel version. This tool is limited to ipv4, and cannot parse tcpflags as bpftrace currently cannot parse socket buffers in the way that bcc can. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace all tcp drops: # .B tcpdrop.bt .TP .SH FIELDS .TP TIME Time of the call, in HH:MM:SS format. .TP PID Process ID that was on-CPU during the drop. This may be unrelated, as drops can occur on the receive interrupt and be unrelated to the PID that was interrupted. .TP COMM Process name .TP SADDR Source IP address. .TP SPORT Source TCP port. .TP DADDR Destination IP address. .TP DPORT Destination TCP port. .TP STATE TCP session state ("ESTABLISHED", etc). .SH OVERHEAD This traces the kernel tcp_drop() function, which should be low frequency, and therefore the overhead of this tool should be negligible. As always, test and understand this tools overhead for your types of workloads before production use. .SH SOURCE This is from bpftrace .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg, adapted for bpftrace by Dale Hamel .SH SEE ALSO tcplife.bt(8), tcpaccept.bt(8), tcpconnect.bt(8), tcptop(8) bpftrace-0.23.2/man/man8/tcplife.bt.8000066400000000000000000000044061477746507000171520ustar00rootroot00000000000000.TH tcplife.bt 8 "2019-07-03" "USER COMMANDS" .SH NAME tcplife.bt \- Trace TCP session lifespans with connection details. Uses bpftrace/eBPF. .SH SYNOPSIS .B tcplife .SH DESCRIPTION This tool shows the lifespan of TCP sessions that open and close while tracing, and shows the duration and throughput statistics. For efficiency, this tool only instruments TCP state changes, rather than all packets. This tool works by using the sock:inet_sock_set_state tracepoint, which was added in Linux 4.16. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF, bpftrace, and the sock:inet_sock_set_state tracepoint (Linux 4.16+). .SH EXAMPLES .TP Show TCP sessions with details: # .B tcplife.bt .SH FIELDS .TP PID Process ID .TP COMM Process name .TP LADDR Local IP address. .TP DADDR Remote IP address. .TP LPORT Local port. .TP RPORT Remote port. .TP TX_KB Total transmitted Kbytes. .TP RX_KB Total received Kbytes. .TP MS Lifespan of the session, in milliseconds. .SH OVERHEAD This traces the kernel TCP set state function, which should be called much less often than send/receive tracing, and therefore have lower overhead. The overhead of the tool is relative to the rate of new TCP sessions: if this is high, over 10,000 per second, then there may be noticeable overhead just to print out 10k lines of formatted output per second. You can find out the rate of new TCP sessions using "sar \-n TCP 1", and adding the active/s and passive/s columns. As always, test and understand this tools overhead for your types of workloads before production use. .SH SOURCE This tool originated from BCC: .IP https://github.com/iovisor/bcc .PP The BCC version has many command line options for customizing the output. .PP This bpftrace version originated from the book "BPF Performance Tools", published by Addison Wesley (2019): .IP http://www.brendangregg.com/bpf-performance-tools-book.html .PP See the book for more documentation on this tool. .PP This bpftrace version is in the bpftrace repository: .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO tcptop(8) bpftrace-0.23.2/man/man8/tcpretrans.bt.8000066400000000000000000000037651477746507000177200ustar00rootroot00000000000000.TH tcpretrans.bt 8 "2018-11-24" "USER COMMANDS" .SH NAME tcpretrans.bt \- Trace or count TCP retransmits. Uses Linux bpftrace/eBPF .SH SYNOPSIS .B tcpretrans.bt .SH DESCRIPTION This traces TCP retransmits, showing address, port, and TCP state information, and sometimes the PID (although usually not, since retransmits are usually sent by the kernel on timeouts). To keep overhead very low, only the TCP retransmit functions are traced. This does not trace every packet (like tcpdump(8) or a packet sniffer). Optionally, it can count retransmits over a user signalled interval to spot potentially dropping network paths the flows are traversing. This uses dynamic tracing of the kernel tcp_retransmit_skb() and tcp_send_loss_probe() functions, and will need to be updated to match kernel changes to these functions. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace TCP retransmits: # .B tcpretrans.bt .TP .SH FIELDS .TP TIME Time of the call, in HH:MM:SS format. .TP PID Process ID that was on-CPU. This is less useful than it might sound, as it may usually be 0, for the kernel, for timer-based retransmits. .TP LADDR Local IP address. .TP LPORT Local port. .TP RADDR Remote IP address. .TP RPORT Remote port. .TP STATE TCP session state. .SH OVERHEAD Should be negligible: TCP retransmit events should be low (<1000/s), and the low overhead this tool adds to each event should make the cost negligible. .SH SOURCE This is from bpftrace .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg, adapted for bpftrace by Dale Hamel .SH SEE ALSO tcpconnect.bt(8), tcpaccept.bt(8) bpftrace-0.23.2/man/man8/tcpsynbl.bt.8000066400000000000000000000035011477746507000173550ustar00rootroot00000000000000.TH tcpsynbl.bt 8 "2019-07-03" "USER COMMANDS" .SH NAME tcpsynbl.bt \- Show the TCP SYN backlog as a histogram. Uses bpftrace/eBPF. .SH SYNOPSIS .B tcpsynbl .SH DESCRIPTION This tool shows the TCP SYN backlog size during SYN arrival as a histogram. This lets you see how close your applications are to hitting the backlog limit and dropping SYNs (causing performance issues with SYN retransmits), and is a measure of workload saturation. The histogram shown is measured at the time of SYN received, and a separate histogram is shown for each backlog limit. This works by tracing the tcp_v4_syn_recv_sock() and tcp_v6_syn_recv_sock() kernel functions using dynamic instrumentation. Since these functions may change in future kernels, this tool may need maintenance to keep working. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Show the TCP SYN backlog as a histogram. # .B tcpsynbl.bt .SH FIELDS .TP backlog The backlog size when a SYN was received. .TP count The number of times this backlog size was encountered. .TP distribution An ASCII visualization of the count column. .SH OVERHEAD Inbound SYNs should be relatively low compared to packets and other events, so the overhead of this tool is expected to be negligible. .SH SOURCE This tool originated from the book "BPF Performance Tools", published by Addison Wesley (2019): .IP http://www.brendangregg.com/bpf-performance-tools-book.html .PP See the book for more documentation on this tool. .PP This version is in the bpftrace repository: .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO tcptop(8) bpftrace-0.23.2/man/man8/threadsnoop.bt.8000066400000000000000000000032031477746507000200440ustar00rootroot00000000000000.TH threadsnoop.bt 8 "2019-07-02" "USER COMMANDS" .SH NAME threadsnoop.bt \- Trace thread creation via pthread_create(). Uses bpftrace/eBPF. .SH SYNOPSIS .B threadsnoop.bt .SH DESCRIPTION threadsnoop traces calls to pthread_create(), showing this path of thread creation. This can be used for workload characterization and discovery, and is a companion to execsnoop.bt(8) which traces execve(2). This works by tracing the pthread_create() from libpthread.so.0. The path to this library may need adjusting in the tool source to match your system. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace calls pthread_create(): # .B threadsnoop.bt .SH FIELDS .TP TIME(ms) Elapsed time since the tool began tracing (in milliseconds). .TP PID The process ID. .TP COMM The process (thread) name. .TP FUNC The name of the start routine, if the symbol is available, else a hex address for the start routine address. .SH OVERHEAD Thread creation is expected to be low (<< 1000/s), so the overhead of this tool is expected to be negligible. .SH SOURCE This tool originated from the book "BPF Performance Tools", published by Addison Wesley (2019): .IP http://www.brendangregg.com/bpf-performance-tools-book.html .PP See the book for more documentation on this tool. .PP This version is in the bpftrace repository: .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO execsnoop.bt(8) bpftrace-0.23.2/man/man8/undump.bt.8000066400000000000000000000023731477746507000170350ustar00rootroot00000000000000.TH undump.bt 8 "2022-06-03" "USER COMMANDS" .SH NAME undump.bt \- Catch UNIX domain socket packages. Uses bpftrace/eBPF. .SH SYNOPSIS .B undump.bt .SH DESCRIPTION undump.bt tracked reception of UNIX domain sockets. This program is also a basic example of bpftrace and kprobes. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace reception of UNIX domain sockets: # .B undump.bt .SH FIELDS .TP TIME A timestamp on the output, in "HH:MM:SS" format. .TP COMM The process COMM. .TP PID The process ID. .TP SIZE The size of the received packet, in bytes. .TP DATA Display received packets in hex or string. .SH OVERHEAD The overhead of this program mainly comes from the data packets received by the terminal output. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc examples/tracing of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Rong Tao .SH SEE ALSO opensnoop.bt(8) bpftrace-0.23.2/man/man8/vfscount.bt.8000066400000000000000000000032311477746507000173660ustar00rootroot00000000000000.TH vfscount.bt 8 "2018-09-06" "USER COMMANDS" .SH NAME vfscount.bt \- Count VFS calls ("vfs_*"). Uses bpftrace/eBPF. .SH SYNOPSIS .B vfscount.bt .SH DESCRIPTION This counts VFS calls. This can be useful for general workload characterization of these operations. This works by tracing all kernel functions beginning with "vfs_" using dynamic tracing. This may match more functions than you are interested in measuring: Edit the script to customize which functions to trace. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Count all VFS calls until Ctrl-C is hit: # .B vfscount.bt .SH FIELDS .TP 1st Kernel function name (in @[]) .TP 2nd Number of calls while tracing .SH OVERHEAD This traces kernel vfs functions and maintains in-kernel counts, which are asynchronously copied to user-space. While the rate of VFS operations can be very high (>1M/sec), this is a relatively efficient way to trace these events, and so the overhead is expected to be small for normal workloads. Measure in a test environment, and if overheads are an issue, edit the script to reduce the types of vfs functions traced (currently all beginning with "vfs_"). .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO vfsstat.bt(8) bpftrace-0.23.2/man/man8/vfsstat.bt.8000066400000000000000000000034351477746507000172170ustar00rootroot00000000000000.TH vfsstat.bt 8 "2018-09-06" "USER COMMANDS" .SH NAME vfsstat.bt \- Count key VFS calls. Uses bpftrace/eBPF. .SH SYNOPSIS .B vfsstat.bt .SH DESCRIPTION This traces some common VFS calls and prints per-second summaries. This can be useful for general workload characterization, and looking for patterns in operation usage over time. This works by tracing some kernel vfs functions using dynamic tracing, and will need updating to match any changes to these functions. Edit the script to customize which functions are traced. Also see vfscount, which is more easily customized to trace multiple functions. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Count some VFS calls, printing per-second summaries until Ctrl-C is hit: # .B vfsstat.bt .SH FIELDS .TP HH:MM:SS Each output summary is prefixed by the time of printing in "HH:MM:SS" format. .TP 1st Kernel function name (in @[]) .TP 2nd Number of calls while tracing .SH OVERHEAD This traces various kernel vfs functions and maintains in-kernel counts, which are asynchronously copied to user-space. While the rate of VFS operations can be very high (>1M/sec), this is a relatively efficient way to trace these events, and so the overhead is expected to be small for normal workloads. Measure in a test environment. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO vfscount.bt(8) bpftrace-0.23.2/man/man8/writeback.bt.8000066400000000000000000000033211477746507000174720ustar00rootroot00000000000000.TH writeback.bt 8 "2018-09-14" "USER COMMANDS" .SH NAME writeback.bt \- Trace file system writeback events with details. Uses bpftrace/eBPF. .SH SYNOPSIS .B writeback.bt .SH DESCRIPTION This traces when file system dirtied pages are flushed to disk by kernel writeback, and prints details including when the event occurred, and the duration of the event. This can be useful for correlating these times with other performance problems, and if there is a match, it would be a clue that the problem may be caused by writeback. How quickly the kernel does writeback can be tuned: see the kernel docs, eg, vm.dirty_writeback_centisecs. This uses the tracepoint:writeback:writeback_start and tracepoint:writeback:writeback_written tracepoints. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace all writeback events with timestamps and latency details: # .B writeback.bt .SH FIELDS .TP TIME Time that the writeback event completed, in %H:%M:%S format. .TP DEVICE Device name in major:minor number format. .TP PAGES Pages written during writeback. .TP REASON Reason for the writeback event. This may be "background", "vmscan", "sync", "periodic", etc. .TP ms Duration of the writeback event in milliseconds. .SH OVERHEAD Since writeback events are expected to be infrequent (<10/sec), the overhead of this tool is expected to be negligible (near 0%). .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO biosnoop.bt(8) bpftrace-0.23.2/man/man8/xfsdist.bt.8000066400000000000000000000034371477746507000172130ustar00rootroot00000000000000.TH xfsdist.bt 8 "2018-09-08" "USER COMMANDS" .SH NAME xfsdist.bt \- Summarize XFS operation latency. Uses bpftrace/eBPF. .SH SYNOPSIS .B xfsdist.bt .SH DESCRIPTION This tool summarizes time (latency) spent in common XFS file operations: reads, writes, opens, and syncs, and presents it as a power-of-2 histogram. It uses an in-kernel eBPF map to store the histogram for efficiency. Since this works by tracing the xfs_file_operations interface functions, it will need updating to match any changes to these functions. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bpftrace. .SH EXAMPLES .TP Trace XFS operation time, and print a summary on Ctrl-C: # .B xfsdist.bt .SH FIELDS .TP 0th The operation name (shown in "@[...]") is printed before each I/O histogram. .TP 1st, 2nd This is a range of latency, in microseconds (shown in "[...)" set notation). .TP 3rd A column showing the count of operations in this range. .TP 4th This is an ASCII histogram representing the count column. .SH OVERHEAD This adds low-overhead instrumentation to these XFS operations, including reads and writes from the file system cache. Such reads and writes can be very frequent (depending on the workload; eg, 1M/sec), at which point the overhead of this tool may become noticeable. Measure and quantify before use. .SH SOURCE This is from bpftrace. .IP https://github.com/bpftrace/bpftrace .PP Also look in the bpftrace distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. This is a bpftrace version of the bcc tool of the same name. The bcc tool may provide more options and customizations. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Brendan Gregg .SH SEE ALSO biolatency.bt(8) bpftrace-0.23.2/scripts/000077500000000000000000000000001477746507000150745ustar00rootroot00000000000000bpftrace-0.23.2/scripts/bash-completion/000077500000000000000000000000001477746507000201605ustar00rootroot00000000000000bpftrace-0.23.2/scripts/bash-completion/bpftrace000066400000000000000000000033331477746507000216730ustar00rootroot00000000000000# bash completion of bpftrace(8) _bpftrace_filedir() { # bash-completion 2.11-1091-g6e8a1546bde2 rename _filedir to _comp_compgen_filedir if [[ ${BASH_COMPLETION_VERSINFO[0]} == 2 ]] && \ [[ ${BASH_COMPLETION_VERSINFO[1]} > 11 ]]; then _comp_compgen_filedir "${@}" else _filedir "${@}" fi } _bpftrace() { local cur prev words argument _init_completion -- "$@" || return local all_args='-B -f -o -e -h --help -I --include -l -p -c --usdt-file-activation --unsafe -q --info -k -V --version --no-warnings -v --dry-run -d --emit-elf --emit-llvm' if [[ $cur == -* ]]; then argument=$cur fi case ${prev} in -B) COMPREPLY=( $(compgen -W "line full none" -- ${cur}) ) return 0 ;; -f) COMPREPLY=( $(compgen -W "text json" -- ${cur}) ) return 0 ;; -c) COMPREPLY=( $(compgen -c -- ${cur}) ) return 0 ;; -o | --include | --emit-elf | --emit-llvm) _bpftrace_filedir return 0 ;; -I) _bpftrace_filedir -d return 0 ;; -p) local PIDS=$(cd /proc && echo [0-9]*) COMPREPLY=( $(compgen -W "$PIDS" -- ${cur}) ) return 0 ;; -d) COMPREPLY=( $(compgen -W "all ast codegen codegen-opt dis libbpf verifier" -- ${cur}) ) return 0 ;; -h | --help | -V | --version | --info) return ;; esac # bpftrace directly specifies the script, like: bpftrace a.bt if [[ ! ${argument} ]]; then _bpftrace_filedir else # Just drop -e content completion, because it's very complex. if ([[ ${cur} == -* ]] || [[ -z ${cur} ]]) && [[ ${prev} != -e ]]; then COMPREPLY=( $(compgen -W "${all_args}" -- ${cur}) ) return fi fi } complete -F _bpftrace bpftrace bpftrace-0.23.2/scripts/check_kernel_features.sh000077500000000000000000000023401477746507000217450ustar00rootroot00000000000000#!/bin/sh # Report missing kernel features # # Usage: ./check_kernel_features.sh [PATH_TO_KERNEL_CONFIG] set -e set -u err=0 config='' # Find kernel config for c in "$@" "/boot/config-$(uname -r)" "/boot/config" "/proc/config.gz"; do if [ -r "$c" ]; then config="$c" break fi done if [ -z "$config" ]; then echo "Could not find kernel config, please supply it as argument." >&2 exit 1 fi # Check feature check_opt() { if ! zgrep -qE "^${1}[[:space:]]*=[[:space:]]*[y|Y]" "$config"; then err=1 echo "Required option ${1} not set" >&2 fi } check_opt 'CONFIG_BPF' check_opt 'CONFIG_BPF_EVENTS' check_opt 'CONFIG_BPF_JIT' check_opt 'CONFIG_BPF_SYSCALL' check_opt 'CONFIG_FTRACE_SYSCALLS' check_opt 'CONFIG_HAVE_EBPF_JIT' check_opt 'CONFIG_FUNCTION_TRACER' check_opt 'CONFIG_HAVE_DYNAMIC_FTRACE' check_opt 'CONFIG_DYNAMIC_FTRACE' check_opt 'CONFIG_HAVE_KPROBES' check_opt 'CONFIG_KPROBES' check_opt 'CONFIG_KPROBE_EVENTS' check_opt 'CONFIG_ARCH_SUPPORTS_UPROBES' check_opt 'CONFIG_UPROBES' check_opt 'CONFIG_UPROBE_EVENTS' check_opt 'CONFIG_DEBUG_FS' # Status report if [ $err -eq 0 ]; then echo "All required features present!" else echo "Missing required features" fi exit $err bpftrace-0.23.2/scripts/clang_tidy.sh000077500000000000000000000035341477746507000175550ustar00rootroot00000000000000#!/bin/bash # Runs clang-tidy against the codebase. # Requires nix. # # Example usage: # # ./scripts/clang_tidy.sh # set -eu shopt -s globstar BUILD_DIR=build-clang-tidy SCRIPT_NAME=$0 FIX= function run() { nix develop --command "$@" } usage() { echo "Usage:" echo " ${SCRIPT_NAME} [OPTIONS]" echo "" echo " Run clang-tidy and optional apply fixes with nix." echo "" echo "OPTIONS" echo " -u Update with fixes." echo " -d Change the default build directory. Default: ${BUILD_DIR}" } while getopts ":d:uh" opt; do case ${opt} in u ) FIX=-fix ;; d ) BUILD_DIR=${OPTARG} ;; h ) usage exit 0 ;; esac done # Change dir to project root cd "$(git rev-parse --show-toplevel)" # Clang-tidy has native integration with compile_commands.json. # Generate it here so that we can teach clang-tidy how to "build" bpftrace. run cmake -B "$BUILD_DIR" -DCMAKE_EXPORT_COMPILE_COMMANDS=1 # We also generate header files here and there. Rather than hard # code which files are generated, just build the entire project # to keep it simple. run make -C "$BUILD_DIR" -j $(nproc) # Note that `run-clang-tidy` comes from `clang` nix package but shells out to # `clang-tidy` and `clang-apply-replacements` from `clang-tools` nix package. # This may just be trivia but seems like a good gotcha to point out. # # Also note we explicitly pass in the files we want to lint. We could # omit the file list but then generated files from flex/bison will start # spewing. # # Also note $FIX is supposed to be unquoted. The problem is if it's quoted # and unset, then it's an empty arg and run-clang-tidy will treat it as # an empty regex which causes all sorts of trouble. run run-clang-tidy -q -format -config-file .clang-tidy -p "$BUILD_DIR" $FIX \ '^.*(src|tests)/.*\.(h|cpp)$' bpftrace-0.23.2/scripts/compare_tool_codegen.sh000077500000000000000000000033261477746507000216060ustar00rootroot00000000000000#!/bin/bash # Compare the IR generated for the shipped # tools between two bpftrace builds # set -o pipefail set -e set -u if [[ "$#" -ne 3 ]]; then echo "Compare IR generated between two bpftrace builds" echo "" echo "USAGE:" echo "$(basename $0) " echo "" echo "EXAMPLE:" echo "$(basename $0) bpftrace bpftrace_master ./tools" echo "" exit 1 fi TOOLDIR=$3 BPF_A=$(command -v "$1") || ( echo "ERROR: $1 not found"; exit 1 ) BPF_B=$(command -v "$2") || ( echo "ERROR: $2 not found"; exit 1 ) [[ -d "$TOOLDIR" ]] || (echo "tooldir does not appear to be a directory: ${TOOLDIR}"; exit 1) # Set to 1 to only compare result after opt AFTER_OPT=0 if [ $AFTER_OPT -eq 1 ]; then FLAGS="-d" else FLAGS="-dd" fi TMPDIR=$(mktemp -d) [[ $? -ne 0 || -z $TMPDIR ]] && (echo "Failed to create tmp dir"; exit 10) cd $TMPDIR set +e function hash() { file="${1}" sha1sum "${1}" | awk '{print $1}' } function fix_timestamp() { cat $@ | awk '/(add|sub) i64 %get_ns/ { $NF = ""} {print}' } echo "Using version $($BPF_A -V) and $($BPF_B -V)" for script in ${TOOLDIR}/*.bt; do s=$(basename ${script/.bt/}) echo "Checking $s" 2>&1 $BPF_A "$FLAGS" "$script" | fix_timestamp > "a_${s}" 2>&1 $BPF_B "$FLAGS" "$script" | fix_timestamp > "b_${s}" if [ $? -ne 0 ]; then echo "###############################" echo "bpftrace failed on script: ${s}" echo "###############################" continue fi if [[ $(hash "a_${s}") != $(hash "b_${s}") ]]; then echo "###############################" echo "Change detected for script: ${s}" diff -b -u "a_${s}" "b_${s}" fi done [[ -n ${TMPDIR} ]] && rm -rf "${TMPDIR}" bpftrace-0.23.2/scripts/compare_tool_elf.sh000077500000000000000000000031551477746507000207500ustar00rootroot00000000000000#!/bin/bash # Compare the ELF generated for the shipped # tools between two bpftrace builds # set -o pipefail set -e set -u if [[ "$#" -ne 4 ]]; then echo "Compare IR generated between two bpftrace builds" echo "" echo "USAGE:" echo "$(basename $0) " echo "" echo "EXAMPLE:" echo "$(basename $0) bpftrace_master bpftrace llvm-objdump-7 ./tools" echo "" exit 1 fi BPF_A=$(command -v "$1") || ( echo "ERROR: $1 not found"; exit 1 ) BPF_B=$(command -v "$2") || ( echo "ERROR: $2 not found"; exit 1 ) OBJDUMP=$(command -v "$3") || (echo "ERROR: $3 not found"; exit 1 ) TOOLDIR=$4 [[ -d "$TOOLDIR" ]] || (echo "tooldir does not appear to be a directory: ${TOOLDIR}"; exit 1) TMPDIR=$(mktemp -d) [[ $? -ne 0 || -z $TMPDIR ]] && (echo "Failed to create tmp dir"; exit 10) cd $TMPDIR set +e function hash() { file="${1}" sha1sum "${1}" | awk '{print $1}' } echo "Using version $($BPF_A -V) and $($BPF_B -V)" for script in ${TOOLDIR}/*.bt; do s=$(basename ${script/.bt/}) echo "Checking $s" 2>&1 $BPF_A --emit-elf "a_${s}" "$script" >/dev/null 2>&1 $BPF_B --emit-elf "b_${s}" "$script" >/dev/null if [ $? -ne 0 ]; then echo "###############################" echo "bpftrace failed on script: ${s}" echo "###############################" continue fi if [[ $(hash "a_${s}") != $(hash "b_${s}") ]]; then echo "###############################" echo "Change detected for script: ${s}" diff -u <($OBJDUMP -S "a_${s}") <($OBJDUMP -S "b_${s}") fi done [[ -n ${TMPDIR} ]] && rm -rf "${TMPDIR}" bpftrace-0.23.2/scripts/compare_tool_speed.sh000077500000000000000000000041251477746507000213000ustar00rootroot00000000000000#!/bin/bash # Compare the processing speed for the shipped # tools between two bpftrace builds # # based on scripts/compare_tool_speed.h # set -o pipefail set -e set -u if [[ "$#" -lt 3 ]]; then echo "Compare tools' speed between two bpftrace builds" echo "" echo "USAGE:" echo "$(basename $0) [] []" echo "" echo "EXAMPLE:" echo "$(basename $0) bpftrace bpftrace_master ./tools" echo "" echo "NOTE: assume that second bpftrace binary is newer" exit 1 fi TOOLDIR=$3 BPF_A=$(command -v "$1") || ( echo "ERROR: $1 not found"; exit 1 ) BPF_B=$(command -v "$2") || ( echo "ERROR: $2 not found"; exit 1 ) [[ -d "$TOOLDIR" ]] || (echo "tooldir does not appear to be a directory: ${TOOLDIR}"; exit 1) TMPDIR=$(mktemp -d) [[ $? -ne 0 || -z $TMPDIR ]] && (echo "Failed to create tmp dir"; exit 10) cd $TMPDIR set +e TESTMODE=${4:-codegen} if [[ $TESTMODE != "codegen" ]]; then echo invalid testmode: $TESTMODE exit 20 fi THRESHOLD=${5:-1} TIME="/usr/bin/time -f%e --" FLAGS="--no-warnings --test $TESTMODE" echo $TESTMODE test echo "Using version $($BPF_A -V) and $($BPF_B -V)" MAXLEN=0 for script in ${TOOLDIR}/*.bt; do s=$(basename ${script/.bt/}) len=${#s} if [[ "$MAXLEN" -lt "$len" ]]; then MAXLEN=$len fi done echo -n " script" for i in `seq 0 1 $((MAXLEN - 6))`; do echo -n ' '; done echo "A B diff" for script in ${TOOLDIR}/*.bt; do s=$(basename ${script/.bt/}) len=${#s} space=$((MAXLEN - len)) echo -n "Checking $s" for i in `seq 0 1 $space`; do echo -n ' '; done a=`$TIME $BPF_A $FLAGS "$script" 3>&1 1>&2 2>&3 3>&-` b=`$TIME $BPF_B $FLAGS "$script" 3>&1 1>&2 2>&3 3>&-` if [ $? -ne 0 ]; then echo "###############################" echo "bpftrace failed on script: ${s}" echo "###############################" continue fi d=$(echo $b - $a | bc) t=$(echo "$d > $THRESHOLD" | bc) mark="" if [[ "$t" -eq 1 ]]; then mark="*" fi echo "$a $b $d$mark" done [[ -n ${TMPDIR} ]] && rm -rf "${TMPDIR}" bpftrace-0.23.2/scripts/create-assets.sh000077500000000000000000000035121477746507000201770ustar00rootroot00000000000000#!/bin/bash # # This script creates builds the official release artifacts. # # Usage examples: # ./scripts/create-assets.sh # OUT=../out-dir ./scripts/create-assets.sh # ZSTDFLAGS="-19" function err() { echo >&2 "$(tput setaf 1)ERROR: $*$(tput sgr0)" exit 1 } function info() { echo "$(tput bold)$*$(tput sgr0)" } [[ -d tools ]] || err "'tools' directory not found, run script from bpftrace root dir" [[ -d man ]] || err "'man' directory not found, run script from bpftrace root dir" command -v zstd >/dev/null 2>&1 || err "zstd command not found, required for release" command -v asciidoctor >/dev/null 2>&1 || err "asciidoctor not found, required for manpage" # Take value from environment if set, otherwise use a tempdir : ${OUT:=$(mktemp -d)} || err "Failed to create temp dir" TMP="${OUT}/tmp" echo "Using '$OUT' as assert dir" set -e info "Creating tools archive" # bit of copying to avoid confusing tar flags not great but works mkdir -p "$TMP/bin" cp tools/*.bt tools/*.txt "$TMP/bin" rm -f "$TMP/bin/CMakeLists.txt" chmod +x "$TMP/bin/"*.bt tar --xz -cf "$OUT/tools-with-help.tar.xz" -C "$TMP/bin" "." rm "$TMP/bin/"*.txt tar --xz -cf "$OUT/tools.tar.xz" -C "$TMP/bin" "." info "Creating man archive" mkdir -p "$TMP/share/man/man8" cp man/man8/*.8 "$TMP/share//man/man8/" gzip -n "$TMP/share/man/man8/"* asciidoctor man/adoc/bpftrace.adoc -b manpage -o - | gzip -n - > "$TMP/share/man/man8/bpftrace.8.gz" tar --xz -cf "$OUT/man.tar.xz" -C "$TMP/share" man info "Building bpftrace appimage" nix build .#appimage info "Creating bundle" cp ./result "$OUT/bpftrace" cp ./result "$TMP/bin/bpftrace" tar -cf "$OUT/binary_tools_man-bundle.tar" -C "$TMP" bin share zstd $ZSTDFLAGS -q -k "$OUT/binary_tools_man-bundle.tar" xz "$OUT/binary_tools_man-bundle.tar" echo "All assets created in $OUT" [[ -d "$TMP" ]] && rm -rf "$TMP" bpftrace-0.23.2/scripts/seccomp.c000066400000000000000000000130001477746507000166630ustar00rootroot00000000000000// Compile with gcc seccomp.c -lseccomp -ggdb -o seccomp #include #include #include #include #include #include #include #include #include void help() { printf("Simulate bpf(2) syscall failures based on the bpf(2) command "); printf("executed\n"); printf("\n"); printf("USAGE:\n"); printf("./seccomp [OPTIONS] -- command [args]"); printf("./seccomp -e map_create:11 -- bpftrace -e ...\n"); printf("./seccomp -e map_create:100 -k prog_load -- ./src/bpftrace -e "); printf("'i:s:1 { @=5 }'\n"); printf("\n"); printf("OPTIONS:\n"); printf("\t-k [NAME] Kill program when bpf is called with this command\n"); printf("\t-e [NAME]:[ERRNO] Set errno to ERRNO program when bpf is called "); printf("with this command\n"); printf("\t-l List known bpf commands\n"); printf("\n"); exit(0); } struct entry { const int value; const char* symbol; const char* name; }; #define ENTRY(symbol, name) \ { \ symbol, #symbol, name \ } static const struct entry bpf_commands[] = { ENTRY(BPF_MAP_CREATE, "map_create"), ENTRY(BPF_MAP_LOOKUP_ELEM, "map_lookup_elem"), ENTRY(BPF_MAP_UPDATE_ELEM, "map_update_elem"), ENTRY(BPF_MAP_DELETE_ELEM, "map_delete_elem"), ENTRY(BPF_MAP_GET_NEXT_KEY, "map_get_next_key"), ENTRY(BPF_PROG_LOAD, "prog_load"), ENTRY(BPF_OBJ_PIN, "obj_pin"), ENTRY(BPF_OBJ_GET, "obj_get"), ENTRY(BPF_PROG_ATTACH, "prog_attach"), ENTRY(BPF_PROG_DETACH, "prog_detach"), ENTRY(BPF_PROG_TEST_RUN, "prog_test_run"), ENTRY(BPF_PROG_GET_NEXT_ID, "prog_get_next_id"), ENTRY(BPF_MAP_GET_NEXT_ID, "map_get_next_id"), ENTRY(BPF_PROG_GET_FD_BY_ID, "prog_get_fd_by_id"), ENTRY(BPF_MAP_GET_FD_BY_ID, "map_get_fd_by_id"), ENTRY(BPF_OBJ_GET_INFO_BY_FD, "obj_get_info_by_fd"), ENTRY(BPF_PROG_QUERY, "prog_query"), ENTRY(BPF_RAW_TRACEPOINT_OPEN, "raw_tracepoint_open"), ENTRY(BPF_BTF_LOAD, "btf_load"), ENTRY(BPF_BTF_GET_FD_BY_ID, "btf_get_fd_by_id"), ENTRY(BPF_TASK_FD_QUERY, "task_fd_query"), }; void list(void) { for (int x = 0; x < sizeof(bpf_commands) / sizeof(struct entry); x++) { printf("name: %s\tflag: %s\n", bpf_commands[x].name, bpf_commands[x].symbol); } exit(0); } // Search the bpf_commands table for an entry for with symbol or name matches // the argument name // // Return the bpf command value or -1 if the lookup failed int lookup_cmd(char* name) { for (int x = 0; x < sizeof(bpf_commands) / sizeof(struct entry); x++) { if (strcmp(name, bpf_commands[x].name) == 0 || strcmp(name, bpf_commands[x].symbol) == 0) { return bpf_commands[x].value; } } return -1; } // Parse the errno string and add the required filter to seccomp int add_errno(scmp_filter_ctx* ctx, char* str) { assert(ctx != NULL); assert(str != NULL); char* substr = strchr(str, ':'); if (substr == NULL) { printf("Expected ERRNO format \"COMMAND:ERRNO\", got: %s\n", str); return -1; } // Copy command to a new string int keysize = substr - str; char* buf = malloc((keysize + 1) * sizeof(char)); assert(buf != NULL); strncpy(buf, str, keysize); // convert ERRNO to positive int int err = atoi(substr + 1); if (err < 0) { err *= -1; } // Find the command ID int command = lookup_cmd(buf); if (command < 0) { printf("Unknown bpf command: %s\n", buf); goto exit; } int rc = seccomp_rule_add(*ctx, SCMP_ACT_ERRNO(err), SCMP_SYS(bpf), 1, SCMP_A0(SCMP_CMP_EQ, command)); if (rc < 0) { printf("Failed to add ERRNO(%d) filter for command: %s(%d): %s\n", err, buf, command, strerror(rc * -1)); } else { printf("Added ERRNO(%d) for command: %s(%d)\n", err, buf, command); } exit: free(buf); } // Parse CLI args, setup seccomp and execve into the command int main(int argc, char** argv) { int index; int c; opterr = 0; scmp_filter_ctx ctx; ctx = seccomp_init(SCMP_ACT_ALLOW); if (ctx == NULL) { printf("Failed to init seccomp\n"); } while ((c = getopt(argc, argv, "k:e:hl")) != -1) { switch (c) { case 'h': help(); break; case 'l': list(); break; case 'k': { int v = lookup_cmd(optarg); if (v >= 0) { int rc = seccomp_rule_add( ctx, SCMP_ACT_KILL, SCMP_SYS(bpf), 1, SCMP_A0(SCMP_CMP_EQ, v)); if (rc < 0) { printf("Failed to add KILL filter for command: %s: %s\n", optarg, strerror(-1 * rc)); } else { printf("Added KILL for command: %s\n", optarg); } } else { printf("Unknown bpf command: %s\n", optarg); } break; } case 'e': add_errno(&ctx, optarg); break; default: printf("Unknown option: %s\n", optarg); break; } } if (argc - optind < 2) { printf("expected command with arguments\n"); goto abort; } seccomp_load(ctx); printf("Executing: "); for (int i = optind; i < argc; i++) { printf("%s ", argv[i]); } printf("\n------------\n\n"); int rc = execve(argv[optind], argv + optind, NULL); printf("Execve failed: %d, %s\n", rc, strerror(errno)); abort: seccomp_release(ctx); } bpftrace-0.23.2/scripts/tracepoint_variable_sized_types.py000066400000000000000000000023621477746507000241100ustar00rootroot00000000000000# This script lists all the types in the kernel's tracepoint format files # which appear with more than one size. This script's output should be # compared to the code in TracepointFormatParser::adjust_integer_types() import glob field_types = {} for format_file_name in glob.iglob("/sys/kernel/debug/tracing/events/*/*/format"): with open(format_file_name) as format_file: for line in format_file: if not line.startswith("\tfield:"): continue size_section = line.split(";")[2].split(":") if size_section[0] != "\tsize": continue size_val = size_section[1] field_section = line.split(";")[0].split(":") if field_section[0] != "\tfield": continue field_val = field_section[1] if "[" in field_val or "*" in field_val: continue field_type = " ".join(field_val.split()[:-1]) if field_type not in field_types: field_types[field_type] = set() field_types[field_type].add(size_val) for t in sorted(field_types): sizes = field_types[t] if len(sizes) > 1: sizes_str = ",".join(sorted(sizes)) print(f"{t}: {sizes_str}") bpftrace-0.23.2/scripts/update_codegen_tests.sh000077500000000000000000000001221477746507000216160ustar00rootroot00000000000000#!/bin/bash echo "This script has been deprecated. Use ./tests/codegen-tests.sh" bpftrace-0.23.2/src/000077500000000000000000000000001477746507000141745ustar00rootroot00000000000000bpftrace-0.23.2/src/CMakeLists.txt000066400000000000000000000170501477746507000167370ustar00rootroot00000000000000if(HAVE_BFD_DISASM) set(BFD_DISASM_SRC bfd-disasm.cpp) endif() if(BUILD_FUZZ) message(STATUS "Build for fuzzing") set(MAIN_SRC fuzz_main.cpp) set(BPFTRACE bpftrace_fuzz) else() set(MAIN_SRC main.cpp) if (NOT DEFINED BPFTRACE) set(BPFTRACE bpftrace) endif () endif() add_library(required_resources required_resources.cpp) add_dependencies(required_resources parser) add_library(compiler_core STATIC functions.cpp struct.cpp types.cpp ) add_dependencies(compiler_core parser) add_library(runtime STATIC attached_probe.cpp bpffeature.cpp bpftrace.cpp bpfbytecode.cpp bpfmap.cpp bpfprogram.cpp btf.cpp child.cpp config.cpp disasm.cpp dwarf_parser.cpp format_string.cpp globalvars.cpp log.cpp output.cpp probe_matcher.cpp procmon.cpp printf.cpp resolve_cgroupid.cpp run_bpftrace.cpp tracefs.cpp debugfs.cpp usdt.cpp utils.cpp pcap_writer.cpp ksyms.cpp usyms.cpp ${BFD_DISASM_SRC} ) # Ensure flex+bison outputs are built first add_dependencies(runtime parser) add_library(libbpftrace STATIC build_info.cpp clang_parser.cpp driver.cpp lockdown.cpp tracepoint_format_parser.cpp ) # So it's not "liblibbpftrace" set_target_properties(libbpftrace PROPERTIES PREFIX "") add_executable(${BPFTRACE} ${MAIN_SRC} ) # TODO: Honor `STATIC_LINKING` properly. if(LIBBLAZESYM_FOUND) target_include_directories(runtime PRIVATE ${LIBBLAZESYM_INCLUDE_DIRS}) target_link_libraries(runtime ${LIBBLAZESYM_LIBRARIES}) endif() install(TARGETS ${BPFTRACE} DESTINATION ${CMAKE_INSTALL_BINDIR}) target_compile_definitions(${BPFTRACE} PRIVATE ${BPFTRACE_FLAGS}) target_link_libraries(${BPFTRACE} libbpftrace) if (BUILD_FUZZ) target_compile_options(${BPFTRACE} PUBLIC "-DFUZZ") if(FUZZ_TARGET STREQUAL "semantic") message(STATUS "Fuzzing seantic analyzer") target_compile_options(${BPFTRACE} PUBLIC -DTEST_SEMANTIC) elseif(FUZZ_TARGET STREQUAL "codegen") message(STATUS "Fuzzing codegen") else() message(FATAL_ERROR "Unsupported FUZZ_TARGET") endif() if (USE_LIBFUZZER) if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") message(FATAL_ERROR "Use Clang for libfuzzer") endif() target_compile_options(${BPFTRACE} PUBLIC "-DLIBFUZZER") message(STATUS "Use libfuzzer") endif() endif() # compile definitions if (KERNEL_HEADERS_DIR) MESSAGE(STATUS "Using KERNEL_HEADERS_DIR=${KERNEL_HEADERS_DIR}") endif() target_compile_definitions(runtime PUBLIC KERNEL_HEADERS_DIR="${KERNEL_HEADERS_DIR}") if (NOT SYSTEM_INCLUDE_PATHS EQUAL "auto") MESSAGE(STATUS "Using SYSTEM_INCLUDE_PATHS=${SYSTEM_INCLUDE_PATHS}") endif() target_compile_definitions(runtime PUBLIC SYSTEM_INCLUDE_PATHS="${SYSTEM_INCLUDE_PATHS}") # This is run on every build to ensure the version string is always up-to-date add_custom_target(version_h COMMAND ${CMAKE_COMMAND} -DVERSION_H_IN=${CMAKE_CURRENT_SOURCE_DIR}/version.h.in -DVERSION_H=${CMAKE_BINARY_DIR}/version.h -Dbpftrace_VERSION_MAJOR=${bpftrace_VERSION_MAJOR} -Dbpftrace_VERSION_MINOR=${bpftrace_VERSION_MINOR} -Dbpftrace_VERSION_PATCH=${bpftrace_VERSION_PATCH} -Dbpftrace_SOURCE_DIR=${CMAKE_SOURCE_DIR} -P ${CMAKE_SOURCE_DIR}/cmake/Version.cmake ) add_dependencies(${BPFTRACE} version_h) add_dependencies(libbpftrace version_h) target_compile_definitions(required_resources PRIVATE ${BPFTRACE_FLAGS}) target_compile_definitions(runtime PRIVATE ${BPFTRACE_FLAGS}) target_include_directories(libbpftrace PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_compile_definitions(libbpftrace PRIVATE ${BPFTRACE_FLAGS}) # Linking if(STATIC_LINKING) target_link_options(${BPFTRACE} BEFORE PRIVATE "-static-libgcc" "-static-libstdc++") endif(STATIC_LINKING) target_link_libraries(runtime ${LIBBPF_LIBRARIES} ${ZLIB_LIBRARIES}) target_link_libraries(libbpftrace parser resources runtime aot ast arch cxxdemangler_llvm) if(LLDB_FOUND) target_link_libraries(runtime LLDB) endif(LLDB_FOUND) if(LIBPCAP_FOUND) target_link_libraries(libbpftrace ${LIBPCAP_LIBRARIES}) endif(LIBPCAP_FOUND) if(HAVE_BFD_DISASM) if(STATIC_LINKING OR LIBBFD_STATIC) add_library(LIBBFD STATIC IMPORTED) set_property(TARGET LIBBFD PROPERTY IMPORTED_LOCATION ${LIBBFD_LIBRARIES}) target_link_libraries(runtime LIBBFD) add_library(LIBOPCODES STATIC IMPORTED) set_property(TARGET LIBOPCODES PROPERTY IMPORTED_LOCATION ${LIBOPCODES_LIBRARIES}) target_link_libraries(runtime LIBOPCODES) add_library(LIBIBERTY STATIC IMPORTED) set_property(TARGET LIBIBERTY PROPERTY IMPORTED_LOCATION ${LIBIBERTY_LIBRARIES}) target_link_libraries(runtime LIBIBERTY) add_library(LIBZSTD STATIC IMPORTED) if (LIBZSTD_LIBRARIES) set_property(TARGET LIBZSTD PROPERTY IMPORTED_LOCATION ${LIBZSTD_LIBRARIES}) target_link_libraries(runtime LIBZSTD) endif(LIBZSTD_LIBRARIES) add_library(LIBSFRAME STATIC IMPORTED) if (LIBSFRAME_LIBRARIES) set_property(TARGET LIBSFRAME PROPERTY IMPORTED_LOCATION ${LIBSFRAME_LIBRARIES}) target_link_libraries(runtime LIBSFRAME) endif(LIBSFRAME_LIBRARIES) else() target_link_libraries(runtime ${LIBBFD_LIBRARIES}) target_link_libraries(runtime ${LIBOPCODES_LIBRARIES}) endif(STATIC_LINKING OR LIBBFD_STATIC) endif(HAVE_BFD_DISASM) # Link to bcc libraries (without LLVM) if possible if(LIBBCC_BPF_CONTAINS_RUNTIME) target_link_libraries(runtime ${LIBBCC_BPF_LIBRARIES}) else() target_link_libraries(runtime ${LIBBCC_LIBRARIES}) endif() if(STATIC_LINKING) # These are not part of the static libbcc so have to be added separate target_link_libraries(runtime ${LIBBCC_BPF_LIBRARIES}) target_link_libraries(runtime ${LIBBPF_LIBRARIES}) target_link_libraries(runtime ${LIBBCC_LOADER_LIBRARY_STATIC}) find_package(LibLzma) add_library(LIBLZMA STATIC IMPORTED) set_property(TARGET LIBLZMA PROPERTY IMPORTED_LOCATION ${LIBLZMA_LIBRARIES}) target_link_libraries(runtime LIBLZMA) add_library(LIBELF STATIC IMPORTED) set_property(TARGET LIBELF PROPERTY IMPORTED_LOCATION ${LIBELF_LIBRARIES}) target_link_libraries(runtime LIBELF) else() target_link_libraries(runtime ${LIBELF_LIBRARIES}) endif(STATIC_LINKING) # Support for std::filesystem # GCC version <9 and Clang (all versions) require -lstdc++fs if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS "9") target_link_libraries(runtime "stdc++fs") target_link_libraries(libbpftrace "stdc++fs") endif() if (BUILD_ASAN) target_compile_options(${BPFTRACE} PUBLIC "-fsanitize=address") target_link_options(${BPFTRACE} PUBLIC "-fsanitize=address") endif() if (USE_LIBFUZZER) if (BUILD_ASAN) target_compile_options(${BPFTRACE} PUBLIC "-fsanitize=fuzzer,address") target_link_options(${BPFTRACE} PUBLIC "-fsanitize=fuzzer") else() target_compile_options(${BPFTRACE} PUBLIC "-fsanitize=fuzzer") target_link_options(${BPFTRACE} PUBLIC "-fsanitize=fuzzer,address") endif() endif() if (STATIC_LINKING) if(ANDROID) target_link_libraries(libbpftrace "-Wl,-Bdynamic" "-ldl" "-lm" "-lz") target_link_libraries(runtime "-Wl,-Bdynamic" "-ldl" "-lm" "-lz") else() target_link_libraries(libbpftrace "-Wl,-Bdynamic" "-lrt" "-lpthread" "-ldl" "-lm") target_link_libraries(libbpftrace "-Wl,-Bstatic" "-lz") target_link_libraries(runtime "-Wl,-Bdynamic" "-lrt" "-lpthread" "-ldl" "-lm") target_link_libraries(runtime "-Wl,-Bstatic" "-lz") endif() endif() if (ENABLE_SYSTEMD) target_link_libraries(runtime PkgConfig::libsystemd) endif() unset(MAIN_SRC) unset(BPFTRACE) add_subdirectory(aot) add_subdirectory(arch) add_subdirectory(ast) add_subdirectory(cxxdemangler) add_subdirectory(resources) bpftrace-0.23.2/src/act_helpers.h000066400000000000000000000044251477746507000166430ustar00rootroot00000000000000#pragma once // Annoying C type helpers. // // C headers sometimes contain struct ending with a flexible array // member, which is not supported in C++. An example of such a type is // file_handle from fcntl.h (man name_to_handle_at) // // Here are some helper macros helping to ensure if the C++ // counterpart has members of the same type and offset. #include #include namespace act_helpers { template M get_member_type(M T::*); } #define ACTH_SAME_SIZE(cxxtype, ctype, extra_type) \ (sizeof(cxxtype) == sizeof(ctype) + sizeof(extra_type)) #define ACTH_GET_TYPE_OF(mem) decltype(act_helpers::get_member_type(&mem)) #define ACTH_SAME_OFFSET(cxxtype, cxxmem, ctype, cmem) \ (std::is_standard_layout::value && \ std::is_standard_layout::value && \ (offsetof(cxxtype, cxxmem) == offsetof(ctype, cmem))) #define ACTH_SAME_TYPE(cxxtype, cxxmem, ctype, cmem) \ std::is_same::value #define ACTH_ASSERT_SAME_SIZE(cxxtype, ctype, extra_type) \ static_assert(ACTH_SAME_SIZE(cxxtype, ctype, extra_type), \ "assumption that is broken: " #cxxtype " == " #ctype \ " + " #extra_type) #define ACTH_ASSERT_SAME_OFFSET(cxxtype, cxxmem, ctype, cmem) \ static_assert(ACTH_SAME_OFFSET(cxxtype, cxxmem, ctype, cmem), \ "assumption that is broken: " #cxxtype "::" #cxxmem \ " is at the same offset as " #ctype "::" #cmem) #define ACTH_ASSERT_SAME_TYPE(cxxtype, cxxmem, ctype, cmem) \ static_assert(ACTH_SAME_TYPE(cxxtype, cxxmem, ctype, cmem), \ "assumption that is broken: " #cxxtype "::" #cxxmem \ " has the same type as " #ctype "::" #cmem) #define ACTH_ASSERT_SAME_MEMBER(cxxtype, cxxmem, ctype, cmem) \ ACTH_ASSERT_SAME_TYPE(cxxtype, cxxmem, ctype, cmem); \ ACTH_ASSERT_SAME_OFFSET(cxxtype, cxxmem, ctype, cmem) bpftrace-0.23.2/src/aot/000077500000000000000000000000001477746507000147575ustar00rootroot00000000000000bpftrace-0.23.2/src/aot/CMakeLists.txt000066400000000000000000000022241477746507000175170ustar00rootroot00000000000000add_library(aot STATIC aot.cpp) add_dependencies(aot version_h) target_link_libraries(aot required_resources parser) target_compile_definitions(aot PRIVATE ${BPFTRACE_FLAGS}) if(STATIC_LINKING) target_link_libraries(aot LIBELF) else() target_link_libraries(aot ${LIBELF_LIBRARIES}) endif(STATIC_LINKING) # Only build aotrt if supported bcc is used # (https://github.com/iovisor/bcc/commit/719191867a25ce07dc96f7faf9b8ccedadc7ec44) if(NOT LIBBCC_BPF_CONTAINS_RUNTIME) return() endif() add_executable(bpftrace-aotrt aot_main.cpp) target_compile_definitions(bpftrace-aotrt PRIVATE ${BPFTRACE_FLAGS}) target_link_libraries(bpftrace-aotrt aot runtime arch ast ast_defs cxxdemangler_stdlib) install(TARGETS bpftrace-aotrt DESTINATION ${CMAKE_INSTALL_BINDIR}) if(LIBPCAP_FOUND) target_link_libraries(bpftrace-aotrt ${LIBPCAP_LIBRARIES}) endif(LIBPCAP_FOUND) # Linking if(STATIC_LINKING) target_link_options(bpftrace-aotrt BEFORE PRIVATE "-static-libgcc" "-static-libstdc++") endif(STATIC_LINKING) # ASAN if(BUILD_ASAN) target_compile_options(bpftrace-aotrt PUBLIC "-fsanitize=address") target_link_options(bpftrace-aotrt PUBLIC "-fsanitize=address") endif() bpftrace-0.23.2/src/aot/aot.cpp000066400000000000000000000223021477746507000162450ustar00rootroot00000000000000#include "aot.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "utils.h" #include "version.h" #define AOT_ELF_SECTION ".btaot" static constexpr auto AOT_MAGIC = 0xA07; static constexpr auto AOT_SECDATA_TEMPFILE = ".temp_btaot"; // AOT payload will have this header at the beginning. We don't worry about // versioning the header b/c we enforce that an AOT compiled script may only // be run with the corresponding runtime shim. We enforce it through the // `version` field, which is the "Robert Sedgwicks hash" of BPFTRACE_VERSION // macro defined in cmake. struct Header { uint16_t magic; // Header magic (can be useful to detect endianness) uint16_t unused; // For future use uint32_t header_len; // Length of this struct uint64_t version; // Hash of version string uint64_t rr_off; // RequiredResources offset from start of file uint64_t rr_len; // RequiredResources length uint64_t elf_off; // ELF offset from start of file uint64_t elf_len; // ELF length }; static_assert(sizeof(Header) == 48); static_assert(sizeof(std::size_t) <= sizeof(uint64_t)); namespace bpftrace::aot { namespace { uint32_t rs_hash(std::string_view str) { unsigned int b = 378551; unsigned int a = 63689; unsigned int hash = 0; for (char c : str) { hash = hash * a + c; a = a * b; } return hash; } int load_required_resources(BPFtrace &bpftrace, uint8_t *ptr, size_t len) { try { bpftrace.resources.load_state(ptr, len); } catch (const std::exception &ex) { LOG(ERROR) << "Failed to deserialize metadata: " << ex.what(); return 1; } return 0; } std::optional> generate_btaot_section( const RequiredResources &resources, void *const elf, size_t elf_size) { // Serialize RuntimeResources std::string serialized_metadata; try { std::ostringstream serialized(std::ios::binary); resources.save_state(serialized); serialized_metadata = serialized.str(); } catch (const std::exception &ex) { LOG(ERROR) << "Failed to serialize runtime metadata: " << ex.what(); return std::nullopt; } // Construct the header auto hdr_len = sizeof(Header); Header hdr = { .magic = AOT_MAGIC, .unused = 0, .header_len = sizeof(Header), .version = rs_hash(BPFTRACE_VERSION), .rr_off = hdr_len, .rr_len = serialized_metadata.size(), .elf_off = hdr_len + serialized_metadata.size(), .elf_len = elf_size, }; // Resize the output buffer appropriately std::vector out; out.resize(sizeof(Header) + hdr.rr_len + hdr.elf_len); uint8_t *p = out.data(); // Write out header memcpy(p, &hdr, sizeof(Header)); p += sizeof(Header); // Write out metadata memcpy(p, serialized_metadata.data(), hdr.rr_len); p += hdr.rr_len; // Write out ELF memcpy(p, elf, hdr.elf_len); p += hdr.elf_len; return out; } // Clones the shim to final destination while also injecting // the custom .btaot section. int build_binary(const std::filesystem::path &shim, const std::string &out, const std::vector §ion) { std::error_code ec; char cmd[1024]; int written; int ret = 0; // Write out section data in a temporary file std::ofstream secdata(AOT_SECDATA_TEMPFILE, std::ios_base::binary); secdata.write(reinterpret_cast(section.data()), section.size()); secdata.close(); // Respect user provided BPFTRACE_OBJCOPY if present std::string_view objcopy = "objcopy"; if (auto c = std::getenv("BPFTRACE_OBJCOPY")) objcopy = c; // Resolve objcopy binary to full path auto objcopy_full = find_in_path(objcopy); if (!objcopy_full) { ret = 1; LOG(ERROR) << "Failed to find " << objcopy << " in $PATH"; goto out; } written = snprintf(cmd, sizeof(cmd), "%s --add-section .btaot=%s %s %s", objcopy_full->c_str(), AOT_SECDATA_TEMPFILE, shim.c_str(), out.c_str()); if (written < 0 || written == sizeof(cmd)) { ret = 1; LOG(ERROR) << "Failed to construct objcopy command"; goto out; } if ((ret = std::system(cmd))) { LOG(ERROR) << "Failed to execute: " << cmd; goto out; } out: if (!std::filesystem::remove(AOT_SECDATA_TEMPFILE, ec) || ec) { ret = 1; LOG(ERROR) << "Failed to remove " << AOT_SECDATA_TEMPFILE << ": " << ec; } return ret; } } // namespace int generate(const RequiredResources &resources, const std::string &out, void *const elf, size_t elf_size) { auto section = generate_btaot_section(resources, elf, elf_size); if (!section) return 1; // For development, we want to use the locally built AOT shim instead of a // "real" one that could be present elsewhere in $PATH. So we give precedence // to shim found "next" to the running binary. auto rel = std::filesystem::path("aot") / AOT_SHIM_NAME; auto local = find_near_self(rel.native()); auto path = find_in_path(AOT_SHIM_NAME); auto shim = local ? local : path; if (!shim) { LOG(ERROR) << "Failed to locate " << AOT_SHIM_NAME << " shim binary. Is it in $PATH?"; return 1; } if (shim == local) LOG(WARNING) << "Using development shim (" << *shim << "). Ensure you are a developer!"; if (auto err = build_binary(*shim, out, *section)) return err; return 0; } int load(BPFtrace &bpftrace, const std::string &in) { int err = 0; int infd = ::open(in.c_str(), O_RDONLY); if (infd < 0) { auto saved_err = errno; LOG(ERROR) << "Failed to open: " << in << ": " << std::strerror(saved_err); return 1; } std::error_code ec; std::filesystem::path in_path{ in }; std::uintmax_t in_file_size = std::filesystem::file_size(in_path, ec); if (in_file_size == static_cast(-1)) { LOG(ERROR) << "Failed to stat: " << in << ": " << ec.message(); return 1; } // Find .btaot section Elf *elf = nullptr; Elf_Scn *scn = nullptr; GElf_Shdr shdr; char *secname = nullptr; Elf_Data *data = nullptr; uint8_t *btaot_section = nullptr; const Header *hdr; if (elf_version(EV_CURRENT) == EV_NONE) { LOG(ERROR) << "Cannot set libelf version: " << elf_errmsg(-1); err = 1; goto out; } elf = elf_begin(infd, ELF_C_READ, nullptr); if (!elf) { LOG(ERROR) << "Cannot read ELF file: " << elf_errmsg(-1); err = 1; goto out; } size_t strndx; if (elf_getshdrstrndx(elf, &strndx) < 0) { LOG(ERROR) << "Failed to get ELF section index of the string table: " << elf_errmsg(-1); err = 1; goto out; } int i; i = 0; while ((scn = elf_nextscn(elf, scn))) { i++; if (!gelf_getshdr(scn, &shdr)) { LOG(ERROR) << "Failed to get ELF section(" << i << ") hdr: " << elf_errmsg(-1); err = 1; goto out; } secname = elf_strptr(elf, strndx, shdr.sh_name); if (!secname) { LOG(ERROR) << "Failed to get ELF section(" << i << ") hdr name: " << elf_errmsg(-1); err = 1; goto out; } if (std::string_view(secname) == AOT_ELF_SECTION) { data = elf_getdata(scn, nullptr); if (!data) { LOG(ERROR) << "Failed to get BTAOT ELF section(" << i << ") data: " << elf_errmsg(-1); err = 1; goto out; } btaot_section = static_cast(data->d_buf); break; } } // Validate .btaot header hdr = reinterpret_cast(btaot_section); if (!hdr) { LOG(ERROR) << "Couldn't find " << AOT_ELF_SECTION << " section in " << in; err = 1; goto out; } if (hdr->magic != AOT_MAGIC) { LOG(ERROR) << "Invalid magic in " << in << ": " << hdr->magic; err = 1; goto out; } if (hdr->unused != 0) { LOG(ERROR) << "Unused bytes are used: " << hdr->unused; err = 1; goto out; } if (hdr->header_len != sizeof(Header)) { LOG(ERROR) << "Invalid header len: " << hdr->header_len; err = 1; goto out; } if (hdr->version != rs_hash(BPFTRACE_VERSION)) { LOG(ERROR) << "Build hash mismatch! " << "Did you build with a different bpftrace version?"; err = 1; goto out; } if ((hdr->rr_off + hdr->rr_len) > static_cast(in_file_size) || (hdr->elf_off + hdr->elf_len) > static_cast(in_file_size)) { LOG(ERROR) << "Corrupted AOT bpftrace file: incomplete payload"; err = 1; goto out; } // Load payloads err = load_required_resources(bpftrace, btaot_section + hdr->rr_off, hdr->rr_len); if (err) goto out; bpftrace.bytecode_ = BpfBytecode{ std::span{ btaot_section + hdr->elf_off, hdr->elf_len } }; if (err) goto out; out: if (elf) elf_end(elf); close(infd); return err; } } // namespace bpftrace::aot bpftrace-0.23.2/src/aot/aot.h000066400000000000000000000007121477746507000157130ustar00rootroot00000000000000#pragma once #include #include #include "bpftrace.h" #include "required_resources.h" namespace bpftrace { namespace aot { static constexpr std::string_view AOT_SHIM_NAME = "bpftrace-aotrt"; int generate(const RequiredResources &resources, const std::string &out, void *const elf, size_t elf_size); int load(BPFtrace &bpftrace, const std::string &in); } // namespace aot } // namespace bpftrace bpftrace-0.23.2/src/aot/aot_main.cpp000066400000000000000000000077321477746507000172630ustar00rootroot00000000000000#include #include #include #include #include "aot.h" #include "bpftrace.h" #include "log.h" #include "output.h" #include "run_bpftrace.h" #include "version.h" using namespace bpftrace; void usage(std::ostream& out, std::string_view filename) { // clang-format off out << "USAGE: " << filename << " [options]" << std::endl; out << std::endl; out << "OPTIONS:" << std::endl; out << " -f FORMAT output format ('text', 'json')" << std::endl; out << " -o file redirect bpftrace output to file" << std::endl; out << " -q, keep messages quiet" << std::endl; out << " -v, verbose messages" << std::endl; out << " -d STAGE debug info for various stages of bpftrace execution" << std::endl; out << " ('all', 'libbpf', 'verifier')" << std::endl; out << " -h, --help show this help message" << std::endl; out << " -V, --version bpftrace version" << std::endl; out << std::endl; // clang-format on return; } std::unique_ptr prepare_output(const std::string& output_file, const std::string& output_format) { std::ostream* os = &std::cout; std::ofstream outputstream; if (!output_file.empty()) { outputstream.open(output_file); if (outputstream.fail()) { LOG(ERROR) << "Failed to open output file: \"" << output_file << "\": " << strerror(errno); return nullptr; } os = &outputstream; } std::unique_ptr output; if (output_format.empty() || output_format == "text") { output = std::make_unique(*os); } else if (output_format == "json") { output = std::make_unique(*os); } else { LOG(ERROR) << "Invalid output format \"" << output_format << "\"\n" << "Valid formats: 'text', 'json'"; return nullptr; } return output; } int main(int argc, char* argv[]) { std::string output_file, output_format; int c; // TODO: which other options from `bpftrace` should be included? const char* const short_opts = "d:f:hVo:qv"; option long_opts[] = { option{ "help", no_argument, nullptr, 'h' }, option{ "version", no_argument, nullptr, 'V' }, option{ nullptr, 0, nullptr, 0 }, // Must be last }; std::filesystem::path p(argv[0]); if (p.filename() == aot::AOT_SHIM_NAME) { LOG(ERROR) << "Runtime shim should not be run directly, please generate a " "binary using --aot option in bpftrace"; return 1; } while ((c = getopt_long(argc, argv, short_opts, long_opts, nullptr)) != -1) { switch (c) { case 'o': output_file = optarg; break; case 'f': output_format = optarg; break; case 'h': usage(std::cout, argv[0]); return 0; case 'V': std::cout << "bpftrace " << BPFTRACE_VERSION << std::endl; return 0; case 'q': bt_quiet = true; break; case 'v': bt_verbose = true; break; case 'd': if (std::strcmp(optarg, "libbpf") == 0) bt_debug.insert(DebugStage::Libbpf); else if (std::strcmp(optarg, "verifier") == 0) bt_debug.insert(DebugStage::Verifier); else if (std::strcmp(optarg, "all") == 0) { bt_debug.insert({ DebugStage::Libbpf, DebugStage::Verifier }); } else { LOG(ERROR) << "USAGE: invalid option for -d: " << optarg; return 1; } break; default: usage(std::cerr, argv[0]); return 1; } } if (argv[optind]) { usage(std::cerr, argv[0]); return 1; } check_is_root(); libbpf_set_print(libbpf_print); auto output = prepare_output(output_file, output_format); if (!output) return 1; BPFtrace bpftrace(std::move(output)); int err = aot::load(bpftrace, argv[0]); if (err) { LOG(ERROR) << "Failed to load AOT script"; return err; } return run_bpftrace(bpftrace, bpftrace.bytecode_); } bpftrace-0.23.2/src/arch/000077500000000000000000000000001477746507000151115ustar00rootroot00000000000000bpftrace-0.23.2/src/arch/CMakeLists.txt000066400000000000000000000014701477746507000176530ustar00rootroot00000000000000if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|aarch64)") add_library(arch STATIC arm.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le") add_library(arch STATIC ppc64.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "s390" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "s390x") add_library(arch STATIC s390.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") add_library(arch STATIC x86_64.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "mips64") add_library(arch STATIC mips64.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") add_library(arch STATIC riscv64.cpp) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "loongarch64") add_library(arch STATIC loongarch64.cpp) else() message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") endif() bpftrace-0.23.2/src/arch/arch.h000066400000000000000000000010041477746507000161720ustar00rootroot00000000000000#pragma once #include #include #include namespace bpftrace { namespace arch { int offset(std::string reg_name); int max_arg(); int arg_offset(int arg_num); int ret_offset(); int pc_offset(); int sp_offset(); int arg_stack_offset(); std::string name(); // Each string is lexicographically sorted by character std::vector invalid_watchpoint_modes(); // Returns the width in bits of kernel pointers. int get_kernel_ptr_width(); } // namespace arch } // namespace bpftrace bpftrace-0.23.2/src/arch/arm.cpp000066400000000000000000000102201477746507000163670ustar00rootroot00000000000000#include "arch.h" #include #include #include #include #include namespace bpftrace { namespace arch { namespace { // clang-format off std::array registers_aarch32 = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr", "pc", "cpsr", }; std::array registers_aarch64 = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "sp", "pc", "pstate", }; // Alternative register names that match struct pt_regs std::array ptrace_registers = { "regs[0]", "regs[1]", "regs[2]", "regs[3]", "regs[4]", "regs[5]", "regs[6]", "regs[7]", "regs[8]", "regs[9]", "regs[10]", "regs[11]", "regs[12]", "regs[13]", "regs[14]", "regs[15]", "regs[16]", "regs[17]", "regs[18]", "regs[19]", "regs[20]", "regs[21]", "regs[22]", "regs[23]", "regs[24]", "regs[25]", "regs[26]", "regs[27]", "regs[28]", "regs[29]", "regs[30]", "sp", "pc", "pstate", }; std::unordered_map compat_offsets = { {"compat_fp", 11}, {"compat_sp", 13}, {"compat_lr", 14}, }; // clang-format on bool is_arm64() { static int ptr_width = get_kernel_ptr_width(); return ptr_width == 64; } int offset_aarch32(const std::string& reg_name) { auto it = find(registers_aarch32.begin(), registers_aarch32.end(), reg_name); if (it == registers_aarch32.end()) return -1; return distance(registers_aarch32.begin(), it); } int offset_aarch64(const std::string& reg_name) { auto it = find(registers_aarch64.begin(), registers_aarch64.end(), reg_name); if (it != registers_aarch64.end()) return distance(registers_aarch64.begin(), it); // Support compat aliases for userspace code executing in the AArch32 state auto it_compat = compat_offsets.find(reg_name); if (it_compat != compat_offsets.end()) return it_compat->second; // Also allow register names that match the fields in struct pt_regs. // These appear in USDT probe arguments. it = find(ptrace_registers.begin(), ptrace_registers.end(), reg_name); if (it != ptrace_registers.end()) return distance(ptrace_registers.begin(), it); return -1; } } // anonymous namespace int offset(std::string reg_name) { // TODO: consider making this based on the execution state bit in pstate return is_arm64() ? offset_aarch64(reg_name) : offset_aarch32(reg_name); } int max_arg() { return (is_arm64() ? 8 : 4) - 1; // r0 to r7 on arm64 } int arg_offset(int arg_num) { // Nth argument is stored at offset N in struct pt_regs return arg_num; } int ret_offset() { return offset("r0"); } int pc_offset() { return offset("pc"); } int sp_offset() { // TODO: this needs to be compat_sp on arm64 if accessing the stack of a // 32-bit process (AArch32), but we don't currently have a way to detect that. return offset("sp"); } int arg_stack_offset() { // SP points to the first argument that is passed on the stack return 0; } std::string name() { return std::string(is_arm64() ? "arm64" : "arm"); } std::vector invalid_watchpoint_modes() { // See arch/arm/kernel/hw_breakpoint.c:arch_build_bp_info in kernel source return std::vector{ "rx", "wx", "rwx", }; } int get_kernel_ptr_width() { // We can't assume that sizeof(void*) in bpftrace is the same as the kernel // pointer size (bpftrace can be compiled as a 32-bit binary and run on a // 64-bit kernel), so we guess based on the machine field of struct utsname. // Note that the uname() syscall can return different values for compat mode // processes (e.g. "armv8l" instead of "aarch64"; see COMPAT_UTS_MACHINE), so // make sure this is taken into account. struct utsname utsname; if (uname(&utsname) >= 0) { if (!strncmp(utsname.machine, "armv", 4) && utsname.machine[4] < '8') return 32; } return 64; } } // namespace arch } // namespace bpftrace bpftrace-0.23.2/src/arch/loongarch64.cpp000066400000000000000000000045321477746507000177470ustar00rootroot00000000000000#include "arch.h" #include "utils.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 = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "orig_a0", "pc", }; // Alternative register names that match struct pt_regs static std::array ptrace_registers = { "regs[0]", "regs[1]", "regs[2]", "regs[3]", "regs[4]", "regs[5]", "regs[6]", "regs[7]", "regs[8]", "regs[9]", "regs[10]", "regs[11]", "regs[12]", "regs[13]", "regs[14]", "regs[15]", "regs[16]", "regs[17]", "regs[18]", "regs[19]", "regs[20]", "regs[21]", "regs[22]", "regs[23]", "regs[24]", "regs[25]", "regs[26]", "regs[27]", "regs[28]", "regs[29]", "regs[30]", "regs[31]", "orig_a0", "csr_era", }; static std::array arg_registers = { "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", }; // clang-format on int offset(std::string reg_name) { auto it = find(registers.begin(), registers.end(), reg_name); if (it == registers.end()) { // Also allow register names that match the fields in struct pt_regs. // These appear in USDT probe arguments. it = find(ptrace_registers.begin(), ptrace_registers.end(), reg_name); if (it == ptrace_registers.end()) { return -1; } return distance(ptrace_registers.begin(), it); } 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("r4"); } int sp_offset() { return offset("r3"); } int arg_stack_offset() { return ARG0_STACK / 8; } std::string name() { return std::string("loongarch64"); } std::vector invalid_watchpoint_modes() { throw FatalUserException( "Watchpoints are not supported on this architecture"); } int get_kernel_ptr_width() { return 64; } } // namespace arch } // namespace bpftrace bpftrace-0.23.2/src/arch/mips64.cpp000066400000000000000000000050121477746507000167350ustar00rootroot00000000000000#include "arch.h" #include "utils.h" #include #include // SP + 8 points to the first argument that is passed on the stack #define ARG0_STACK 8 namespace bpftrace { namespace arch { // clang-format off static std::array registers = { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp/s8", "ra", }; // Alternative register names that match struct pt_regs static std::array ptrace_registers = { "regs[0]", "regs[1]", "regs[2]", "regs[3]", "regs[4]", "regs[5]", "regs[6]", "regs[7]", "regs[8]", "regs[9]", "regs[10]", "regs[11]", "regs[12]", "regs[13]", "regs[14]", "regs[15]", "regs[16]", "regs[17]", "regs[18]", "regs[19]", "regs[20]", "regs[21]", "regs[22]", "regs[23]", "regs[24]", "regs[25]", "regs[26]", "regs[27]", "regs[28]", "regs[29]", "regs[30]", "regs[31]", }; 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()) { // Also allow register names that match the fields in struct pt_regs. // These appear in USDT probe arguments. it = find(ptrace_registers.begin(), ptrace_registers.end(), reg_name); if (it == ptrace_registers.end()) { return -1; } return distance(ptrace_registers.begin(), it); } 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)); } #pragma GCC push_options #pragma GCC optimize("O0") static int *__getpc(void) { int *rtaddr; __asm__ volatile("move %0, $31" : "=r"(rtaddr)); return rtaddr; } #pragma GCC pop_options int pc_offset() { int *retAddr, pc; retAddr = __getpc(); pc = *retAddr; return pc; } int ret_offset() { return offset("v0"); } int sp_offset() { return offset("sp"); } int arg_stack_offset() { return ARG0_STACK / 8; } std::string name() { return std::string("mips64"); } std::vector invalid_watchpoint_modes() { throw FatalUserException( "Watchpoints are not supported on this architecture"); } int get_kernel_ptr_width() { return 64; } } // namespace arch } // namespace bpftrace bpftrace-0.23.2/src/arch/ppc64.cpp000066400000000000000000000047051477746507000165570ustar00rootroot00000000000000#include "arch.h" #include #include #include #include #define ARG_REGISTERS 8 // For little endian 64 bit, sp + 32 + 8 regs save area + argX #define ARG0_STACK_LE 96 // For big endian 64 bit, sp + 48 + 8 regs save area + argX #define ARG0_STACK_BE 112 namespace bpftrace { namespace arch { // clang-format off static std::vector> registers = { { "r0", "gpr[0]" }, { "r1", "gpr[1]" }, { "r2", "gpr[2]" }, { "r3", "gpr[3]" }, { "r4", "gpr[4]" }, { "r5", "gpr[5]" }, { "r6", "gpr[6]" }, { "r7", "gpr[7]" }, { "r8", "gpr[8]" }, { "r9", "gpr[9]" }, { "r10", "gpr[10]" }, { "r11", "gpr[11]" }, { "r12", "gpr[12]" }, { "r13", "gpr[13]" }, { "r14", "gpr[14]" }, { "r15", "gpr[15]" }, { "r16", "gpr[16]" }, { "r17", "gpr[17]" }, { "r18", "gpr[18]" }, { "r19", "gpr[19]" }, { "r20", "gpr[20]" }, { "r21", "gpr[21]" }, { "r22", "gpr[22]" }, { "r23", "gpr[23]" }, { "r24", "gpr[24]" }, { "r25", "gpr[25]" }, { "r26", "gpr[26]" }, { "r27", "gpr[27]" }, { "r28", "gpr[28]" }, { "r29", "gpr[29]" }, { "r30", "gpr[30]" }, { "r31", "gpr[31]" }, { "nip" }, { "msr" }, { "orig_gpr3" }, { "ctr" }, { "link" }, { "xer" }, { "ccr" }, { "softe" }, { "trap" }, { "dar" }, { "dsisr" }, { "result" }, }; static std::array arg_registers = { "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", }; // clang-format on int offset(std::string reg_name) { for (unsigned int i = 0; i < registers.size(); i++) { if (registers[i].count(reg_name)) return i; } return -1; } int max_arg() { return arg_registers.size() - 1; } int arg_offset(int arg_num) { return offset(arg_registers.at(arg_num)); } int ret_offset() { return offset("r3"); } int pc_offset() { return offset("nip"); } int sp_offset() { return offset("r1"); } int arg_stack_offset() { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ return ARG0_STACK_LE / 8; #else return ARG0_STACK_BE / 8; #endif } std::string name() { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ return std::string("ppc64le"); #else return std::string("ppc64"); #endif // __BYTE_ORDER__ } std::vector invalid_watchpoint_modes() { // See PowerISA Book III v3.1B, Section 5.4.4 and 10.4 return std::vector{ "x", "rx", "wx", "rwx", }; } int get_kernel_ptr_width() { return 64; } } // namespace arch } // namespace bpftrace bpftrace-0.23.2/src/arch/riscv64.cpp000066400000000000000000000027021477746507000171160ustar00rootroot00000000000000#include "arch.h" #include "utils.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 FatalUserException( "Watchpoints are not supported on this architecture"); } int get_kernel_ptr_width() { return 64; } } // namespace arch } // namespace bpftrace bpftrace-0.23.2/src/arch/s390.cpp000066400000000000000000000033711477746507000163170ustar00rootroot00000000000000#include "arch.h" #include "utils.h" #include #include #include #include #define ARG_REGISTERS 5 // For s390x, r2-r6 registers are used as function arguments, then the extra // arguments can be found starting at sp+160 #define ARG0_STACK 160 namespace bpftrace { namespace arch { // clang-format off static std::vector> registers = { // Breakpoint event address { "arg" }, { "pswmask" }, // Instruction address { "pswaddr" }, { "r0", "gprs[0]" }, { "r1", "gprs[1]" }, { "r2", "gprs[2]" }, { "r3", "gprs[3]" }, { "r4", "gprs[4]" }, { "r5", "gprs[5]" }, { "r6", "gprs[6]" }, { "r7", "gprs[7]" }, { "r8", "gprs[8]" }, { "r9", "gprs[9]" }, { "r10", "gprs[10]" }, { "r11", "gprs[11]" }, { "r12", "gprs[12]" }, { "r13", "gprs[13]" }, { "r14", "gprs[14]" }, { "r15", "gprs[15]" } }; static std::array arg_registers = { "r2", "r3", "r4", "r5", "r6", }; // clang-format on int offset(std::string reg_name) { for (unsigned int i = 0; i < registers.size(); i++) { if (registers[i].count(reg_name)) return i; } return -1; } int max_arg() { return arg_registers.size() - 1; } int arg_offset(int arg_num) { return offset(arg_registers.at(arg_num)); } int ret_offset() { return offset("r2"); } int pc_offset() { return offset("pswaddr"); } int sp_offset() { return offset("r15"); } int arg_stack_offset() { return ARG0_STACK / 8; } std::string name() { return std::string("s390x"); } std::vector invalid_watchpoint_modes() { throw FatalUserException( "Watchpoints are not supported on this architecture"); } int get_kernel_ptr_width() { return 64; } } // namespace arch } // namespace bpftrace bpftrace-0.23.2/src/arch/x86_64.cpp000066400000000000000000000025501477746507000165550ustar00rootroot00000000000000#include "arch.h" #include #include // SP + 8 points to the first argument that is passed on the stack #define ARG0_STACK 8 namespace bpftrace::arch { // clang-format off static std::array registers = { "r15", "r14", "r13", "r12", "bp", "bx", "r11", "r10", "r9", "r8", "ax", "cx", "dx", "si", "di", "orig_ax", "ip", "cs", "flags", "sp", "ss", }; static std::array arg_registers = { "di", "si", "dx", "cx", "r8", "r9", }; // 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 ret_offset() { return offset("ax"); } int pc_offset() { return offset("ip"); } int sp_offset() { return offset("sp"); } int arg_stack_offset() { return ARG0_STACK / 8; } std::string name() { return std::string("x86_64"); } std::vector invalid_watchpoint_modes() { // See intel developer manual, Volume 3, section 17.2.4 return std::vector{ "r", "rx", "wx", "rwx", }; } int get_kernel_ptr_width() { return 64; } } // namespace bpftrace::arch bpftrace-0.23.2/src/ast/000077500000000000000000000000001477746507000147635ustar00rootroot00000000000000bpftrace-0.23.2/src/ast/CMakeLists.txt000066400000000000000000000045441477746507000175320ustar00rootroot00000000000000add_library(ast_defs STATIC ast.cpp) add_dependencies(ast_defs parser) add_library(ast STATIC async_event_types.cpp attachpoint_parser.cpp codegen_helper.cpp dibuilderbpf.cpp int_parser.cpp irbuilderbpf.cpp pass_manager.cpp signal.cpp passes/codegen_resources.cpp passes/config_analyser.cpp passes/field_analyser.cpp passes/portability_analyser.cpp passes/printer.cpp passes/resource_analyser.cpp passes/semantic_analyser.cpp passes/codegen_llvm.cpp passes/return_path_analyser.cpp ) add_dependencies(ast parser) target_compile_definitions(ast PRIVATE ${BPFTRACE_FLAGS}) target_link_libraries(ast PUBLIC ast_defs arch compiler_core parser) if(STATIC_LINKING) include(Util) set(clang_libs clangAST clangAnalysis clangBasic clangCodeGen clangDriver clangEdit clangFormat clangFrontend clangIncludeFixerPlugin clangIndex clangLex clangParse clangRewrite clangSema clangSerialization clangTidyPlugin clangToolingCore) set(llvm_lib_names bpfcodegen coverage frontenddriver frontendhlsl frontendopenmp ipo irreader lto mcjit option orcjit ${LLVM_TARGETS_TO_BUILD}) llvm_map_components_to_libnames(llvm_libs ${llvm_lib_names}) # It's extremely unclear why the clang static libraries export link # dependencies on the llvm and clang shared libraries. Regardless, we need # this hack to remove them. unlink_transitive_dependency("${CLANG_EXPORTED_TARGETS}" "LLVM") unlink_transitive_dependency("${CLANG_EXPORTED_TARGETS}" "$") if(TARGET libclang_static) target_link_libraries(ast PUBLIC libclang_static) else() # old LLVM versions don't export libclang_static in ClangTargets.cmake; fall back to # libclang.a in that case target_link_libraries(ast PUBLIC libclang.a) endif() target_link_libraries(ast PUBLIC ${clang_libs}) target_link_libraries(ast PUBLIC ${llvm_libs}) else() # llvm_config macro comes from the LLVM toolchain and will auto-resolve component # names to library names. USE_SHARED option will tell llvm_config macro to prefer # shared library / DLL on the system over the static libraries llvm_config(ast USE_SHARED bpfcodegen ipo irreader mcjit orcjit) target_link_libraries(ast PUBLIC libclang) endif() bpftrace-0.23.2/src/ast/ast.cpp000066400000000000000000000303121477746507000162550ustar00rootroot00000000000000#include "ast/ast.h" #include #include "ast/visitor.h" #include "log.h" namespace bpftrace::ast { static constexpr std::string_view ENUM = "enum "; Integer::Integer(int64_t n, location loc, bool is_negative) : Expression(loc), n(n), is_negative(is_negative) { is_literal = true; } String::String(const std::string &str, location loc) : Expression(loc), str(str) { is_literal = true; } StackMode::StackMode(const std::string &mode, location loc) : Expression(loc), mode(mode) { is_literal = true; } Builtin::Builtin(const std::string &ident, location loc) : Expression(loc), ident(is_deprecated(ident)) { } Identifier::Identifier(const std::string &ident, location loc) : Expression(loc), ident(ident) { } PositionalParameter::PositionalParameter(PositionalParameterType ptype, long n, location loc) : Expression(loc), ptype(ptype), n(n) { is_literal = true; } Call::Call(const std::string &func, location loc) : Expression(loc), func(is_deprecated(func)) { } Call::Call(const std::string &func, ExpressionList &&vargs, location loc) : Expression(loc), func(is_deprecated(func)), vargs(std::move(vargs)) { } Sizeof::Sizeof(SizedType type, location loc) : Expression(loc), argtype(type) { } Sizeof::Sizeof(Expression *expr, location loc) : Expression(loc), expr(expr) { } Offsetof::Offsetof(SizedType record, std::vector &field, location loc) : Expression(loc), record(record), field(field) { } Offsetof::Offsetof(Expression *expr, std::vector &field, location loc) : Expression(loc), expr(expr), field(field) { } Map::Map(const std::string &ident, location loc) : Expression(loc), ident(ident) { is_map = true; } Map::Map(const std::string &ident, Expression &expr, location loc) : Expression(loc), ident(ident), key_expr(&expr) { is_map = true; key_expr->key_for_map = this; } Variable::Variable(const std::string &ident, location loc) : Expression(loc), ident(ident) { is_variable = true; } Binop::Binop(Expression *left, Operator op, Expression *right, location loc) : Expression(loc), left(left), right(right), op(op) { } Unop::Unop(Operator op, Expression *expr, bool is_post_op, location loc) : Expression(loc), expr(expr), op(op), is_post_op(is_post_op) { } Ternary::Ternary(Expression *cond, Expression *left, Expression *right, location loc) : Expression(loc), cond(cond), left(left), right(right) { } FieldAccess::FieldAccess(Expression *expr, const std::string &field, location loc) : Expression(loc), expr(expr), field(field) { } FieldAccess::FieldAccess(Expression *expr, ssize_t index, location loc) : Expression(loc), expr(expr), index(index) { } ArrayAccess::ArrayAccess(Expression *expr, Expression *indexpr, location loc) : Expression(loc), expr(expr), indexpr(indexpr) { } Cast::Cast(SizedType cast_type, Expression *expr, location loc) : Expression(loc), expr(expr) { type = cast_type; } Tuple::Tuple(ExpressionList &&elems, location loc) : Expression(loc), elems(std::move(elems)) { } ExprStatement::ExprStatement(Expression *expr, location loc) : Statement(loc), expr(expr) { } AssignMapStatement::AssignMapStatement(Map *map, Expression *expr, location loc) : Statement(loc), map(map), expr(expr) { expr->map = map; }; AssignVarStatement::AssignVarStatement(Variable *var, Expression *expr, location loc) : Statement(loc), var(var), expr(expr) { expr->var = var; } AssignVarStatement::AssignVarStatement(VarDeclStatement *var_decl_stmt, Expression *expr, location loc) : Statement(loc), var_decl_stmt(var_decl_stmt), var(var_decl_stmt->var), expr(expr) { expr->var = var; } AssignConfigVarStatement::AssignConfigVarStatement( const std::string &config_var, Expression *expr, location loc) : Statement(loc), config_var(config_var), expr(expr) { } VarDeclStatement::VarDeclStatement(Variable *var, SizedType type, location loc) : Statement(loc), var(var), set_type(true) { var->type = std::move(type); } VarDeclStatement::VarDeclStatement(Variable *var, location loc) : Statement(loc), var(var) { var->type = CreateNone(); } Predicate::Predicate(Expression *expr, location loc) : Node(loc), expr(expr) { } AttachPoint::AttachPoint(const std::string &raw_input, bool ignore_invalid, location loc) : Node(loc), raw_input(raw_input), ignore_invalid(ignore_invalid) { } Block::Block(StatementList &&stmts, location loc) : Statement(loc), stmts(std::move(stmts)) { } If::If(Expression *cond, Block *if_block, Block *else_block, location loc) : Statement(loc), cond(cond), if_block(if_block), else_block(else_block) { } Unroll::Unroll(Expression *expr, Block *block, location loc) : Statement(loc), expr(expr), block(block) { } Probe::Probe(AttachPointList &&attach_points, Predicate *pred, Block *block, location loc) : Node(loc), attach_points(std::move(attach_points)), pred(pred), block(block) { } SubprogArg::SubprogArg(std::string name, SizedType type, location loc) : Node(loc), type(std::move(type)), name_(std::move(name)) { } std::string SubprogArg::name() const { return name_; } Subprog::Subprog(std::string name, SizedType return_type, SubprogArgList &&args, StatementList &&stmts, location loc) : Node(loc), args(std::move(args)), return_type(std::move(return_type)), stmts(std::move(stmts)), name_(std::move(name)) { } Program::Program(const std::string &c_definitions, Config *config, SubprogList &&functions, ProbeList &&probes, location loc) : Node(loc), c_definitions(c_definitions), config(config), functions(std::move(functions)), probes(std::move(probes)) { } std::string opstr(const Jump &jump) { switch (jump.ident) { case JumpType::RETURN: return "return"; case JumpType::BREAK: return "break"; case JumpType::CONTINUE: return "continue"; default: return {}; } return {}; // unreached } std::string opstr(const Binop &binop) { switch (binop.op) { case Operator::EQ: return "=="; case Operator::NE: return "!="; case Operator::LE: return "<="; case Operator::GE: return ">="; case Operator::LT: return "<"; case Operator::GT: return ">"; case Operator::LAND: return "&&"; case Operator::LOR: return "||"; case Operator::LEFT: return "<<"; case Operator::RIGHT: return ">>"; case Operator::PLUS: return "+"; case Operator::MINUS: return "-"; case Operator::MUL: return "*"; case Operator::DIV: return "/"; case Operator::MOD: return "%"; case Operator::BAND: return "&"; case Operator::BOR: return "|"; case Operator::BXOR: return "^"; default: return {}; } return {}; // unreached } std::string opstr(const Unop &unop) { switch (unop.op) { case Operator::LNOT: return "!"; case Operator::BNOT: return "~"; case Operator::MINUS: return "-"; case Operator::MUL: return "dereference"; case Operator::INCREMENT: if (unop.is_post_op) return "++ (post)"; return "++ (pre)"; case Operator::DECREMENT: if (unop.is_post_op) return "-- (post)"; return "-- (pre)"; default: return {}; } return {}; // unreached } AttachPoint &AttachPoint::create_expansion_copy(ASTContext &ctx, const std::string &match) const { // Create a new node with the same raw tracepoint. We initialize all the // information about the attach point, and then override/reset values // depending on the specific probe type. auto &ap = *ctx.make_node(raw_input, ignore_invalid, loc); ap.index_ = index_; ap.provider = provider; ap.target = target; ap.lang = lang; ap.ns = ns; ap.func = func; ap.pin = pin; ap.usdt = usdt; ap.freq = freq; ap.len = len; ap.mode = mode; ap.async = async; ap.expansion = expansion; ap.address = address; ap.func_offset = func_offset; switch (probetype(ap.provider)) { case ProbeType::kprobe: case ProbeType::kretprobe: ap.func = match; if (match.find(":") != std::string::npos) ap.target = erase_prefix(ap.func); break; case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::fentry: case ProbeType::fexit: case ProbeType::tracepoint: // Tracepoint, uprobe, and fentry/fexit probes specify both a target // (category for tracepoints, binary for uprobes, and kernel module // for fentry/fexit and a function name. ap.func = match; ap.target = erase_prefix(ap.func); break; case ProbeType::usdt: // USDT probes specify a target binary path, a provider, and a function // name. ap.func = match; ap.target = erase_prefix(ap.func); ap.ns = erase_prefix(ap.func); break; case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: // Watchpoint probes come with target prefix. Strip the target to get the // function ap.func = match; erase_prefix(ap.func); break; case ProbeType::rawtracepoint: ap.func = match; break; case ProbeType::software: case ProbeType::hardware: case ProbeType::interval: case ProbeType::profile: case ProbeType::special: case ProbeType::iter: case ProbeType::invalid: break; default: LOG(BUG) << "Unknown probe type"; } return ap; } std::string AttachPoint::name() const { std::string n = provider; if (target != "") n += ":" + target; if (lang != "") n += ":" + lang; if (ns != "") n += ":" + ns; if (func != "") { n += ":" + func; if (func_offset != 0) n += "+" + std::to_string(func_offset); } if (address != 0) n += ":" + std::to_string(address); if (freq != 0) n += ":" + std::to_string(freq); if (len != 0) n += ":" + std::to_string(len); if (mode.size()) n += ":" + mode; return n; } int AttachPoint::index() const { return index_; } void AttachPoint::set_index(int index) { index_ = index; } std::string Probe::name() const { std::vector ap_names; std::transform(attach_points.begin(), attach_points.end(), std::back_inserter(ap_names), [](const AttachPoint *ap) { return ap->name(); }); return str_join(ap_names, ","); } std::string Probe::args_typename() const { return "struct " + name() + "_args"; } int Probe::index() const { return index_; } void Probe::set_index(int index) { index_ = index; } std::string Subprog::name() const { return name_; } bool Probe::has_ap_of_probetype(ProbeType probe_type) { for (auto *ap : attach_points) { if (probetype(ap->provider) == probe_type) return true; } return false; } SizedType ident_to_record(const std::string &ident, int pointer_level) { SizedType result = CreateRecord(ident, std::weak_ptr()); for (int i = 0; i < pointer_level; i++) result = CreatePointer(result); return result; } SizedType ident_to_sized_type(const std::string &ident) { if (ident.starts_with(ENUM)) { auto enum_name = ident.substr(ENUM.size()); // This is an automatic promotion to a uint64 // even though it's possible that highest variant value of that enum // fits into a smaller int. This will also affect casts from a smaller // int and cause an ERROR: Integer size mismatch. // This could potentially be revisited or the cast relaxed // if we check the variant values during semantic analysis. return CreateEnum(64, enum_name); } return ident_to_record(ident); } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/ast.h000066400000000000000000000306011477746507000157230ustar00rootroot00000000000000#pragma once #include #include #include #include #include "location.hh" #include "types.h" #include "usdt.h" #include "utils.h" namespace bpftrace { namespace ast { class ASTContext; enum class JumpType { INVALID = 0, RETURN, CONTINUE, BREAK, }; enum class Operator { INVALID = 0, ASSIGN, EQ, NE, LE, GE, LEFT, RIGHT, LT, GT, LAND, LOR, PLUS, INCREMENT, DECREMENT, MINUS, MUL, DIV, MOD, BAND, BOR, BXOR, LNOT, BNOT, }; // There are 2 kinds of attach point expansion: // - full expansion - separate LLVM function is generated for each match // - multi expansion - one LLVM function and BPF program is generated for all // matches, the list of expanded functions is attached to // the BPF program using the k(u)probe.multi mechanism enum class ExpansionType { NONE, FULL, MULTI, }; class Node { public: Node(location loc) : loc(loc) {}; virtual ~Node() = default; Node(const Node &) = delete; Node &operator=(const Node &) = delete; Node(Node &&) = delete; Node &operator=(Node &&) = delete; location loc; }; class Map; class Variable; class Expression : public Node { public: Expression(location loc) : Node(loc) {}; SizedType type; Map *key_for_map = nullptr; Map *map = nullptr; // Only set when this expression is assigned to a map Variable *var = nullptr; // Set when this expression is assigned to a variable bool is_literal = false; bool is_variable = false; bool is_map = false; }; using ExpressionList = std::vector; class Integer : public Expression { public: explicit Integer(int64_t n, location loc, bool is_negative = true); int64_t n; bool is_negative; }; class PositionalParameter : public Expression { public: explicit PositionalParameter(PositionalParameterType ptype, long n, location loc); PositionalParameterType ptype; long n; bool is_in_str = false; }; class String : public Expression { public: explicit String(const std::string &str, location loc); std::string str; }; class StackMode : public Expression { public: explicit StackMode(const std::string &mode, location loc); std::string mode; }; class Identifier : public Expression { public: explicit Identifier(const std::string &ident, location loc); std::string ident; }; class Builtin : public Expression { public: explicit Builtin(const std::string &ident, location loc); std::string ident; int probe_id; // Check if the builtin is 'arg0' - 'arg9' bool is_argx() const { return !ident.compare(0, 3, "arg") && ident.size() == 4 && ident.at(3) >= '0' && ident.at(3) <= '9'; } }; class Call : public Expression { public: explicit Call(const std::string &func, location loc); Call(const std::string &func, ExpressionList &&vargs, location loc); std::string func; ExpressionList vargs; }; class Sizeof : public Expression { public: Sizeof(SizedType type, location loc); Sizeof(Expression *expr, location loc); Expression *expr = nullptr; SizedType argtype; }; class Offsetof : public Expression { public: Offsetof(SizedType record, std::vector &field, location loc); Offsetof(Expression *expr, std::vector &field, location loc); SizedType record; Expression *expr = nullptr; std::vector field; }; class Map : public Expression { public: explicit Map(const std::string &ident, location loc); Map(const std::string &ident, Expression &expr, location loc); std::string ident; Expression *key_expr = nullptr; SizedType key_type; bool skip_key_validation = false; // This is for a feature check on reading per-cpu maps // which involve calling map_lookup_percpu_elem // https://github.com/bpftrace/bpftrace/issues/3755 bool is_read = true; }; class Variable : public Expression { public: explicit Variable(const std::string &ident, location loc); std::string ident; }; class Binop : public Expression { public: Binop(Expression *left, Operator op, Expression *right, location loc); Expression *left = nullptr; Expression *right = nullptr; Operator op; }; class Unop : public Expression { public: Unop(Operator op, Expression *expr, bool is_post_op, location loc); Expression *expr = nullptr; Operator op; bool is_post_op; }; class FieldAccess : public Expression { public: FieldAccess(Expression *expr, const std::string &field); FieldAccess(Expression *expr, const std::string &field, location loc); FieldAccess(Expression *expr, ssize_t index, location loc); Expression *expr = nullptr; std::string field; ssize_t index = -1; }; class ArrayAccess : public Expression { public: ArrayAccess(Expression *expr, Expression *indexpr); ArrayAccess(Expression *expr, Expression *indexpr, location loc); Expression *expr = nullptr; Expression *indexpr = nullptr; }; class Cast : public Expression { public: Cast(SizedType type, Expression *expr, location loc); Expression *expr = nullptr; }; class Tuple : public Expression { public: Tuple(ExpressionList &&elems, location loc); ExpressionList elems; }; class Statement : public Node { public: Statement(location loc) : Node(loc) {}; }; using StatementList = std::vector; class ExprStatement : public Statement { public: explicit ExprStatement(Expression *expr, location loc); Expression *expr = nullptr; }; class VarDeclStatement : public Statement { public: VarDeclStatement(Variable *var, SizedType type, location loc); VarDeclStatement(Variable *var, location loc); Variable *var = nullptr; bool set_type = false; }; class AssignMapStatement : public Statement { public: AssignMapStatement(Map *map, Expression *expr, location loc); Map *map = nullptr; Expression *expr = nullptr; }; class AssignVarStatement : public Statement { public: AssignVarStatement(Variable *var, Expression *expr, location loc); AssignVarStatement(VarDeclStatement *var_decl_stmt, Expression *expr, location loc); VarDeclStatement *var_decl_stmt = nullptr; Variable *var = nullptr; Expression *expr = nullptr; }; class AssignConfigVarStatement : public Statement { public: AssignConfigVarStatement(const std::string &config_var, Expression *expr, location loc); std::string config_var; Expression *expr = nullptr; }; class Block : public Statement { public: Block(StatementList &&stmts, location loc); StatementList stmts; }; class If : public Statement { public: If(Expression *cond, Block *if_block, Block *else_block, location loc); Expression *cond = nullptr; Block *if_block = nullptr; Block *else_block = nullptr; }; class Unroll : public Statement { public: Unroll(Expression *expr, Block *block, location loc); long int var = 0; Expression *expr = nullptr; Block *block = nullptr; }; class Jump : public Statement { public: Jump(JumpType ident, Expression *return_value, location loc) : Statement(loc), ident(ident), return_value(return_value) { } Jump(JumpType ident, location loc) : Statement(loc), ident(ident), return_value(nullptr) { } JumpType ident = JumpType::INVALID; Expression *return_value; }; class Predicate : public Node { public: explicit Predicate(Expression *expr, location loc); Expression *expr = nullptr; }; class Ternary : public Expression { public: Ternary(Expression *cond, Expression *left, Expression *right, location loc); Expression *cond = nullptr; Expression *left = nullptr; Expression *right = nullptr; }; class While : public Statement { public: While(Expression *cond, Block *block, location loc) : Statement(loc), cond(cond), block(block) { } Expression *cond = nullptr; Block *block = nullptr; }; class For : public Statement { public: For(Variable *decl, Expression *expr, StatementList &&stmts, location loc) : Statement(loc), decl(decl), expr(expr), stmts(std::move(stmts)) { } Variable *decl = nullptr; Expression *expr = nullptr; StatementList stmts; SizedType ctx_type; }; class Config : public Statement { public: Config(StatementList &&stmts, location loc) : Statement(loc), stmts(std::move(stmts)) {}; StatementList stmts; }; class AttachPoint : public Node { public: AttachPoint(const std::string &raw_input, bool ignore_invalid, location loc); // Currently, the AST node itself is used to store metadata related to probe // expansion and attachment. This is done through `create_expansion_copy` // below. Since the nodes are not currently copyable by default (this is // currently fraught, as nodes may have backreferences that are not updated // in these cases), these fields are copied manually. *Until this is fixed, // if you are adding new fields, be sure to update `create_expansion_copy`. // Raw, unparsed input from user, eg. kprobe:vfs_read std::string raw_input; std::string provider; std::string target; std::string lang; // for userspace probes, enable language-specific features std::string ns; std::string func; std::string pin; usdt_probe_entry usdt; // resolved USDT entry, used to support arguments with // wildcard matches int64_t freq = 0; uint64_t len = 0; // for watchpoint probes, the width of watched addr std::string mode; // for watchpoint probes, the watch mode bool async = false; // for watchpoint probes, if it's an async watchpoint ExpansionType expansion = ExpansionType::NONE; uint64_t address = 0; uint64_t func_offset = 0; bool ignore_invalid = false; std::string name() const; AttachPoint &create_expansion_copy(ASTContext &ctx, const std::string &match) const; int index() const; void set_index(int index); private: int index_ = 0; }; using AttachPointList = std::vector; class Probe : public Node { public: Probe(AttachPointList &&attach_points, Predicate *pred, Block *block, location loc); AttachPointList attach_points; Predicate *pred = nullptr; Block *block = nullptr; std::string name() const; std::string args_typename() const; bool need_expansion = false; // must build a BPF program per wildcard match int tp_args_structs_level = -1; // number of levels of structs that must // be imported/resolved for tracepoints int index() const; void set_index(int index); bool has_ap_of_probetype(ProbeType probe_type); private: int index_ = 0; }; using ProbeList = std::vector; class SubprogArg : public Node { public: SubprogArg(std::string name, SizedType type, location loc); std::string name() const; SizedType type; private: std::string name_; }; using SubprogArgList = std::vector; class Subprog : public Node { public: Subprog(std::string name, SizedType return_type, SubprogArgList &&args, StatementList &&stmts, location loc); SubprogArgList args; SizedType return_type; StatementList stmts; std::string name() const; private: std::string name_; }; using SubprogList = std::vector; class Program : public Node { public: Program(const std::string &c_definitions, Config *config, SubprogList &&functions, ProbeList &&probes, location loc); std::string c_definitions; Config *config = nullptr; SubprogList functions; ProbeList probes; }; std::string opstr(const Binop &binop); std::string opstr(const Unop &unop); std::string opstr(const Jump &jump); SizedType ident_to_record(const std::string &ident, int pointer_level = 0); SizedType ident_to_sized_type(const std::string &ident); template concept NodeType = std::derived_from; // Manages the lifetime of AST nodes. // // Nodes allocated by an ASTContext will be kept alive for the duration of the // owning ASTContext object. class ASTContext { public: Program *root = nullptr; // Creates and returns a pointer to an AST node. template T *make_node(Args &&...args) { auto uniq_ptr = std::make_unique(std::forward(args)...); auto *raw_ptr = uniq_ptr.get(); nodes_.push_back(std::move(uniq_ptr)); return raw_ptr; } unsigned int node_count() { return nodes_.size(); } private: std::vector> nodes_; }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/async_event_types.cpp000066400000000000000000000047461477746507000212440ustar00rootroot00000000000000#include "ast/async_event_types.h" #include #include #include "ast/irbuilderbpf.h" namespace bpftrace::AsyncEvent { std::vector Print::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt32Ty(), // map id b.getInt32Ty(), // top b.getInt32Ty(), // div }; } std::vector PrintNonMap::asLLVMType(ast::IRBuilderBPF& b, size_t size) { return { b.getInt64Ty(), // asyncid b.getInt64Ty(), // print id llvm::ArrayType::get(b.getInt8Ty(), size), // content }; } std::vector MapEvent::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt32Ty(), // map id }; } std::vector Time::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt32Ty(), // time id }; } std::vector Strftime::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt32Ty(), // strftime id b.getInt32Ty(), // timestamp mode b.getInt64Ty(), // strftime arg, nanoseconds }; } std::vector Buf::asLLVMType(ast::IRBuilderBPF& b, uint32_t length) { return { b.getInt32Ty(), // buffer length llvm::ArrayType::get(b.getInt8Ty(), length), // buffer content }; } std::vector HelperError::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt64Ty(), // error_id b.getInt32Ty(), // return value }; } std::vector Watchpoint::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt64Ty(), // watchpoint_idx b.getInt64Ty(), // addr }; } std::vector WatchpointUnwatch::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt64Ty(), // addr }; } std::vector CgroupPath::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // cgroup path (pseudo-event) id b.getInt64Ty(), // cgroup id }; } std::vector SkbOutput::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt64Ty(), // skb_output id b.getInt64Ty(), // time elapsed since boot }; } std::vector Exit::asLLVMType(ast::IRBuilderBPF& b) { return { b.getInt64Ty(), // asyncid b.getInt8Ty(), // exit_code }; } } // namespace bpftrace::AsyncEvent bpftrace-0.23.2/src/ast/async_event_types.h000066400000000000000000000057541477746507000207110ustar00rootroot00000000000000#pragma once #include #include #include // Forward declare some LLVM types here. We do not #include any LLVM // headers b/c that will pull in LLVM's ABI checking machinery. We want // this header to be independent of LLVM during link time so that AOT // does not need to link against LLVM. namespace llvm { class Type; } // namespace llvm namespace bpftrace { namespace ast { class IRBuilderBPF; } // namespace ast } // namespace bpftrace // The main goal here is to keep the struct definitions close to each other, // making it easier to spot type mismatches. // // If you update a type, remember to update the .cpp too! namespace bpftrace { namespace AsyncEvent { struct Print { uint64_t action_id; uint32_t mapid; uint32_t top; uint32_t div; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct PrintNonMap { uint64_t action_id; uint64_t print_id; // See below why we don't use a flexible length array uint8_t content[0]; std::vector asLLVMType(ast::IRBuilderBPF& b, size_t size); } __attribute__((packed)); struct MapEvent { uint64_t action_id; uint32_t mapid; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct Time { uint64_t action_id; uint32_t time_id; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct Strftime { uint32_t strftime_id; uint32_t mode; uint64_t nsecs; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct Buf { uint32_t length; // Seems like GCC 7.4.x can't handle `char content[]`. Work around by using // 0 sized array (a GCC extension that clang also accepts: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70932). It also looks like // the issue doesn't exist in GCC 7.5.x. char content[0]; std::vector asLLVMType(ast::IRBuilderBPF& b, uint32_t length); } __attribute__((packed)); struct HelperError { uint64_t action_id; uint64_t error_id; int32_t return_value; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct Watchpoint { uint64_t action_id; uint64_t watchpoint_idx; uint64_t addr; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct WatchpointUnwatch { uint64_t action_id; uint64_t addr; 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)); struct SkbOutput { uint64_t action_id; uint64_t skb_output_id; uint64_t nsecs_since_boot; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); struct Exit { uint64_t action_id; uint8_t exit_code; std::vector asLLVMType(ast::IRBuilderBPF& b); } __attribute__((packed)); } // namespace AsyncEvent } // namespace bpftrace bpftrace-0.23.2/src/ast/async_ids.h000066400000000000000000000047531477746507000171210ustar00rootroot00000000000000#pragma once namespace bpftrace { namespace ast { // Add new ids here #define FOR_LIST_OF_ASYNC_IDS(DO) \ DO(cat) \ DO(cgroup_path) \ DO(helper_error) \ DO(join) \ DO(bpf_print) \ DO(non_map_print) \ DO(printf) \ DO(map_key) \ DO(read_map_value) \ DO(skb_output) \ DO(strftime) \ DO(str) \ DO(system) \ DO(time) \ DO(tuple) \ DO(variable) \ DO(watchpoint) #define DEFINE_MEMBER_VAR(id, ...) int _##id = 0; #define DEFINE_ACCESS_METHOD(id, ...) \ int id() \ { \ return _##id++; \ } #define LOCAL_SAVE(id, ...) local_##id = _##id, #define LOCAL_RESTORE(id, ...) this->_##id = local_##id; class AsyncIds { public: explicit AsyncIds() = default; FOR_LIST_OF_ASYNC_IDS(DEFINE_ACCESS_METHOD) // For 'create_reset_ids' return a lambda that has captured-by-value // CodegenLLVM's async id state. Running the returned lambda will restore // `CodegenLLVM`s async id state back to when this function was first called. std::function create_reset_ids() { return [FOR_LIST_OF_ASYNC_IDS(LOCAL_SAVE) this] { FOR_LIST_OF_ASYNC_IDS(LOCAL_RESTORE) }; } private: FOR_LIST_OF_ASYNC_IDS(DEFINE_MEMBER_VAR) }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/attachpoint_parser.cpp000066400000000000000000000511131477746507000213620ustar00rootroot00000000000000#include "ast/attachpoint_parser.h" #include "ast.h" #include "ast/int_parser.h" #include "log.h" #include "types.h" #include #include #include #include #include #include #include #include namespace bpftrace::ast { AttachPointParser::State AttachPointParser::argument_count_error( int expected, std::optional expected2) { // Subtract one for the probe type (eg kprobe) int found = parts_.size() - 1; errs_ << ap_->provider << " probe type requires " << expected; if (expected2.has_value()) { errs_ << " or " << *expected2; } errs_ << " arguments, found " << found << std::endl; return INVALID; } std::optional AttachPointParser::stoull(const std::string &str) { try { return int_parser::to_uint(str, 0); } catch (const std::exception &e) { errs_ << e.what() << std::endl; return std::nullopt; } } std::optional AttachPointParser::stoll(const std::string &str) { try { return int_parser::to_int(str, 0); } catch (const std::exception &e) { errs_ << e.what() << std::endl; return std::nullopt; } } AttachPointParser::AttachPointParser(ASTContext &ctx, BPFtrace &bpftrace, std::ostream &sink, bool listing) : ctx_(ctx), bpftrace_(bpftrace), sink_(sink), listing_(listing) { } int AttachPointParser::parse() { if (!ctx_.root) return 1; uint32_t failed = 0; for (Probe *probe : ctx_.root->probes) { for (size_t i = 0; i < probe->attach_points.size(); ++i) { auto ap_ptr = probe->attach_points[i]; auto &ap = *ap_ptr; new_attach_points.clear(); State s = parse_attachpoint(ap); if (s == INVALID) { ++failed; LOG(ERROR, ap.loc, sink_) << errs_.str(); } else if (s == SKIP || s == NEW_APS) { // Remove the current attach point probe->attach_points.erase(probe->attach_points.begin() + i); i--; if (s == NEW_APS) { // The removed attach point is replaced by new ones probe->attach_points.insert(probe->attach_points.end(), new_attach_points.begin(), new_attach_points.end()); } } // clear error buffer between attach points to prevent non-fatal errors // from being carried over and printed on the next fatal error errs_.str({}); } 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; } AttachPointParser::State AttachPointParser::parse_attachpoint(AttachPoint &ap) { ap_ = ≈ parts_.clear(); if (State s = lex_attachpoint(*ap_)) return s; if (parts_.empty()) { errs_ << "Invalid attachpoint definition" << std::endl; 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())) { // Single argument listing looks at all relevant probe types std::string probetype_query = (parts_.size() == 1) ? "*" : parts_.front(); // Probe type expansion // If PID is specified or the second part of the attach point is a path // (contains '/'), use userspace probe types. // Otherwise, use kernel probe types. if (bpftrace_.pid() > 0 || (parts_.size() >= 2 && parts_[1].find('/') != std::string::npos)) { probe_types = bpftrace_.probe_matcher_->expand_probetype_userspace( probetype_query); } else { probe_types = bpftrace_.probe_matcher_->expand_probetype_kernel( probetype_query); } } else probe_types = { parts_.front() }; if (probe_types.empty()) { if (has_wildcard(parts_.front())) errs_ << "No probe type matched for " << parts_.front() << std::endl; else errs_ << "Invalid probe type: " << parts_.front() << std::endl; return INVALID; } else if (probe_types.size() > 1) { // If the probe type string matches more than 1 probe, create a new set of // attach points (one for every match) that will replace the original one. for (const auto &probe_type : probe_types) { std::string raw_input = ap.raw_input; if (parts_.size() > 1) erase_prefix(raw_input); raw_input = probe_type + ":" + raw_input; // New attach points have ignore_invalid set to true - probe types for // which raw_input has invalid number of parts will be ignored (instead // of throwing an error). These will have the same associated location. new_attach_points.push_back( ctx_.make_node(raw_input, true, ap.loc)); } return NEW_APS; } ap.provider = expand_probe_name(*probe_types.begin()); switch (probetype(ap.provider)) { case ProbeType::special: return special_parser(); case ProbeType::kprobe: return kprobe_parser(); case ProbeType::kretprobe: return kretprobe_parser(); case ProbeType::uprobe: return uprobe_parser(); case ProbeType::uretprobe: return uretprobe_parser(); case ProbeType::usdt: return usdt_parser(); case ProbeType::tracepoint: return tracepoint_parser(); case ProbeType::profile: return profile_parser(); case ProbeType::interval: return interval_parser(); case ProbeType::software: return software_parser(); case ProbeType::hardware: return hardware_parser(); case ProbeType::watchpoint: return watchpoint_parser(); case ProbeType::asyncwatchpoint: return watchpoint_parser(true); case ProbeType::fentry: case ProbeType::fexit: return fentry_parser(); case ProbeType::iter: return iter_parser(); case ProbeType::rawtracepoint: return raw_tracepoint_parser(); case ProbeType::invalid: errs_ << "Invalid probe type: " << ap.provider << std::endl; return INVALID; } __builtin_unreachable(); } AttachPointParser::State AttachPointParser::lex_attachpoint( const AttachPoint &ap) { std::string raw = ap.raw_input; std::vector ret; bool in_quotes = false; std::string argument; for (size_t idx = 0; idx < raw.size(); ++idx) { if (raw[idx] == ':' && !in_quotes) { parts_.emplace_back(std::move(argument)); // The standard says an std::string in moved-from state is in // valid but unspecified state, so clear() to be safe argument.clear(); } else if (raw[idx] == '"') in_quotes = !in_quotes; // Handle escaped characters in a string else if (in_quotes && raw[idx] == '\\' && (idx + 1 < raw.size())) { argument += raw[idx + 1]; ++idx; } else if (!in_quotes && raw[idx] == '$') { // There's an assumption that the positional parameter is well // formed. ie we are not expecting a bare `$` or `$nonint`. The // bison parser should have guaranteed this. size_t i = idx + 1; size_t len = 0; while (i < raw.size() && (raw[i] != '"' && raw[i] != ':')) { len++; i++; } std::string param_idx_str = raw.substr(idx + 1, len); size_t pos, param_idx; param_idx = std::stoll(param_idx_str, &pos, 0); if (pos != param_idx_str.size()) { errs_ << "Found trailing text '" << param_idx_str.substr(pos) << "' in positional parameter index. Try quoting the trailing text." << std::endl; return State::INVALID; } // Expand the positional param in-place and decrement idx so that the next // iteration takes the first char of the expansion raw = raw.substr(0, idx) + bpftrace_.get_param(param_idx, true) + raw.substr(i); idx--; } else argument += raw[idx]; } // Add final argument // // There will always be text in `argument` unless the AP definition // ended in a ':' which we will treat as an empty argument. parts_.emplace_back(std::move(argument)); return State::OK; } AttachPointParser::State AttachPointParser::special_parser() { // Can only have reached here if provider is `BEGIN` or `END` or `self` assert(ap_->provider == "BEGIN" || ap_->provider == "END" || ap_->provider == "self"); if (ap_->provider == "BEGIN" || ap_->provider == "END") { if (parts_.size() == 2 && parts_[1] == "*") parts_.pop_back(); if (parts_.size() != 1) { return argument_count_error(0); } } else if (ap_->provider == "self") { if (parts_.size() != 3) { return argument_count_error(2); } ap_->target = parts_[1]; ap_->func = parts_[2]; } return OK; } AttachPointParser::State AttachPointParser::kprobe_parser(bool allow_offset) { auto num_parts = parts_.size(); if (num_parts != 2 && num_parts != 3) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(1, 2); } auto func_idx = 1; if (num_parts == 3) { ap_->target = parts_[1]; func_idx = 2; } // Handle kprobe:func+0x100 case auto plus_count = std::count(parts_[func_idx].cbegin(), parts_[func_idx].cend(), '+'); if (plus_count) { if (!allow_offset) { errs_ << "Offset not allowed" << std::endl; return INVALID; } if (plus_count != 1) { errs_ << "Cannot take more than one offset" << std::endl; return INVALID; } auto offset_parts = split_string(parts_[func_idx], '+', true); if (offset_parts.size() != 2) { errs_ << "Invalid offset" << std::endl; return INVALID; } ap_->func = offset_parts[0]; auto res = stoll(offset_parts[1]); if (!res) { errs_ << "Invalid offset" << std::endl; return INVALID; } ap_->func_offset = *res; } // Default case (eg kprobe:func) else { ap_->func = parts_[func_idx]; } // kprobe_multi does not support the "module:function" syntax so in case // a module is specified, always use full expansion if (has_wildcard(ap_->target)) ap_->expansion = ExpansionType::FULL; else if (has_wildcard(ap_->func)) { if (ap_->target.empty() && bpftrace_.feature_->has_kprobe_multi()) { ap_->expansion = ExpansionType::MULTI; } else { ap_->expansion = ExpansionType::FULL; } } return OK; } AttachPointParser::State AttachPointParser::kretprobe_parser() { return kprobe_parser(false); } AttachPointParser::State AttachPointParser::uprobe_parser(bool allow_offset, bool allow_abs_addr) { if (bpftrace_.pid() > 0 && (parts_.size() == 2 || (parts_.size() == 3 && is_supported_lang(parts_[1])))) { // For PID, the target may be skipped if (parts_.size() == 2) parts_.insert(parts_.begin() + 1, ""); auto target = get_pid_exe(bpftrace_.pid()); parts_[1] = path_for_pid_mountns(bpftrace_.pid(), target); } if (parts_.size() != 3 && parts_.size() != 4) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(2, 3); } if (parts_.size() == 4) ap_->lang = parts_[2]; ap_->target = ""; if (!has_wildcard(parts_[1]) && parts_[1].find("lib") == 0) { // Automatic resolution of shared library paths. // If the target has form "libXXX" then we use BCC to find the correct path // to the given library as it may differ across systems. auto libname = parts_[1].substr(3); auto lib_path = bcc_procutils_which_so(libname.c_str(), bpftrace_.pid()); if (lib_path) { ap_->target = lib_path; ::free(lib_path); } } if (ap_->target.empty()) { ap_->target = parts_[1]; } const std::string &func = parts_.back(); // Handle uprobe:/lib/asdf:func+0x100 case auto plus_count = std::count(func.cbegin(), func.cend(), '+'); if (plus_count) { if (!allow_offset) { errs_ << "Offset not allowed" << std::endl; return INVALID; } if (plus_count != 1) { errs_ << "Cannot take more than one offset" << std::endl; return INVALID; } auto offset_parts = split_string(func, '+', true); if (offset_parts.size() != 2) { errs_ << "Invalid offset" << std::endl; return INVALID; } ap_->func = offset_parts[0]; auto res = stoll(offset_parts[1]); if (!res) { errs_ << "Invalid offset" << std::endl; return INVALID; } ap_->func_offset = *res; } // Default case (eg uprobe:[addr][func]) else { if (allow_abs_addr) { auto res = stoll(func); if (res) { if (has_wildcard(ap_->target)) { errs_ << "Cannot use wildcards with absolute address" << std::endl; return INVALID; } ap_->address = *res; } else { ap_->address = 0; ap_->func = func; } } else ap_->func = func; } // As the C++ language supports function overload, a given function name // (without parameters) could have multiple matches even when no // wildcards are used. if (has_wildcard(ap_->func) || has_wildcard(ap_->target) || ap_->lang == "cpp") { if (bpftrace_.feature_->has_uprobe_multi()) { ap_->expansion = ExpansionType::MULTI; } else { ap_->expansion = ExpansionType::FULL; } } return OK; } AttachPointParser::State AttachPointParser::uretprobe_parser() { return uprobe_parser(false); } AttachPointParser::State AttachPointParser::usdt_parser() { if (bpftrace_.pid() > 0) { // For PID, the target can be skipped if (parts_.size() == 2) { parts_.push_back(parts_[1]); parts_[1] = ""; } } if (parts_.size() != 3 && parts_.size() != 4) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(2, 3); } if (parts_.size() == 3) { ap_->target = parts_[1]; ap_->func = parts_[2]; } else { ap_->target = parts_[1]; ap_->ns = parts_[2]; ap_->func = parts_[3]; } // Always fully expand USDT probes as they may access args if (has_wildcard(ap_->target) || has_wildcard(ap_->ns) || ap_->ns.empty() || has_wildcard(ap_->func) || bpftrace_.pid()) ap_->expansion = ExpansionType::FULL; return OK; } AttachPointParser::State AttachPointParser::tracepoint_parser() { // Help with `bpftrace -l 'tracepoint:*foo*'` listing -- wildcard the // tracepoint category b/c user is most likely to be looking for the event // name if (parts_.size() == 2 && has_wildcard(parts_.at(1))) parts_.insert(parts_.begin() + 1, "*"); if (parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(2); } ap_->target = parts_[1]; ap_->func = parts_[2]; if (ap_->target.find('*') != std::string::npos || ap_->func.find('*') != std::string::npos) ap_->expansion = ExpansionType::FULL; return OK; } AttachPointParser::State AttachPointParser::profile_parser() { if (parts_.size() == 2 && has_wildcard(parts_[1])) { // Wildcards are allowed for listing ap_->target = parts_[1]; ap_->freq = 0; return OK; } if (parts_.size() != 3) { return argument_count_error(2); } ap_->target = parts_[1]; auto res = stoull(parts_[2]); if (!res) { errs_ << "Invalid rate of " << ap_->provider << " probe"; return INVALID; } ap_->freq = *res; return OK; } AttachPointParser::State AttachPointParser::interval_parser() { if (parts_.size() == 2 && has_wildcard(parts_[1])) { // Wildcards are allowed for listing ap_->target = parts_[1]; ap_->freq = 0; return OK; } if (parts_.size() != 3) { return argument_count_error(2); } ap_->target = parts_[1]; auto res = stoull(parts_[2]); if (!res) { errs_ << "Invalid rate of " << ap_->provider << " probe"; return INVALID; } ap_->freq = *res; return OK; } AttachPointParser::State AttachPointParser::software_parser() { if (parts_.size() != 2 && parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(1, 2); } ap_->target = parts_[1]; if (parts_.size() == 3 && parts_[2] != "*") { auto res = stoull(parts_[2]); if (!res) { errs_ << "Invalid count for " << ap_->provider << " probe"; return INVALID; } ap_->freq = *res; } return OK; } AttachPointParser::State AttachPointParser::hardware_parser() { if (parts_.size() != 2 && parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(1, 2); } ap_->target = parts_[1]; if (parts_.size() == 3 && parts_[2] != "*") { auto res = stoull(parts_[2]); if (!res) { errs_ << "Invalid count for " << ap_->provider << " probe"; return INVALID; } ap_->freq = *res; } return OK; } AttachPointParser::State AttachPointParser::watchpoint_parser(bool async) { if (parts_.size() != 4) { return argument_count_error(3); } if (parts_[1].find('+') == std::string::npos) { auto parsed = stoull(parts_[1]); if (!parsed) { errs_ << "Invalid function/address argument" << std::endl; return INVALID; } ap_->address = *parsed; } else { auto func_arg_parts = split_string(parts_[1], '+', true); if (func_arg_parts.size() != 2) { errs_ << "Invalid function/address argument" << std::endl; return INVALID; } ap_->func = func_arg_parts[0]; if (ap_->func.find('*') != std::string::npos) ap_->expansion = ExpansionType::FULL; if (func_arg_parts[1].size() <= 3 || func_arg_parts[1].find("arg") != 0) { errs_ << "Invalid function argument" << std::endl; return INVALID; } auto parsed = stoull(func_arg_parts[1].substr(3)); if (!parsed) { errs_ << "Invalid function argument" << std::endl; return INVALID; } ap_->address = *parsed; } auto len_parsed = stoull(parts_[2]); if (!len_parsed) { errs_ << "Invalid length argument" << std::endl; return INVALID; } ap_->len = *len_parsed; // Semantic analyser will ensure a cmd/pid was provided ap_->target = bpftrace_.get_watchpoint_binary_path().value_or(""); ap_->mode = parts_[3]; ap_->async = async; return OK; } AttachPointParser::State AttachPointParser::fentry_parser() { // fentry[:module]:function if (parts_.size() != 2 && parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(1, 2); } if (parts_.size() == 3) { ap_->target = parts_[1]; ap_->func = parts_[2]; } else { ap_->func = parts_[1]; if (ap_->func.find('*') == std::string::npos) { auto func_modules = bpftrace_.get_func_modules(ap_->func); if (func_modules.size() == 1) ap_->target = *func_modules.begin(); else if (func_modules.size() > 1) { if (listing_) ap_->target = "*"; else { // Attaching to multiple functions of the same name is currently // broken, ask the user to specify a module explicitly. errs_ << "ambiguous attach point, please specify module containing " "the function \'" << ap_->func << "\'"; return INVALID; } } } else // leave the module empty for now ap_->target = "*"; } if (ap_->func.find('*') != std::string::npos || ap_->target.find('*') != std::string::npos) ap_->expansion = ExpansionType::FULL; return OK; } AttachPointParser::State AttachPointParser::iter_parser() { if (parts_.size() != 2 && parts_.size() != 3) { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type takes 2 arguments (1 optional)" << std::endl; return INVALID; } if (parts_[1].find('*') != std::string::npos) { if (listing_) { ap_->expansion = ExpansionType::FULL; } else { if (ap_->ignore_invalid) return SKIP; errs_ << ap_->provider << " probe type does not support wildcards" << std::endl; return INVALID; } } ap_->func = parts_[1]; if (parts_.size() == 3) ap_->pin = parts_[2]; return OK; } AttachPointParser::State AttachPointParser::raw_tracepoint_parser() { if (parts_.size() != 2) { if (ap_->ignore_invalid) return SKIP; return argument_count_error(1); } ap_->func = parts_[1]; if (has_wildcard(ap_->func)) ap_->expansion = ExpansionType::FULL; return OK; } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/attachpoint_parser.h000066400000000000000000000040151477746507000210260ustar00rootroot00000000000000#pragma once #include #include #include #include #include "ast/ast.h" #include "bpftrace.h" namespace bpftrace { namespace ast { class AttachPointParser { public: AttachPointParser(ASTContext &ctx, BPFtrace &bpftrace, std::ostream &sink, bool listing); ~AttachPointParser() = default; int parse(); private: enum State { OK = 0, INVALID, NEW_APS, SKIP }; State parse_attachpoint(AttachPoint &ap); // This method splits an attach point definition into arguments, // where arguments are separated by `:`. The exception is `:`s inside // of quoted strings, which we must treat as a literal. // // This method also resolves positional parameters. Positional params // may be escaped with double quotes. // // Note that this function assumes the raw string is generally well // formed. More specifically, that there is no unescaped whitespace // and no unmatched quotes. State lex_attachpoint(const AttachPoint &ap); State special_parser(); State kprobe_parser(bool allow_offset = true); State kretprobe_parser(); State uprobe_parser(bool allow_offset = true, bool allow_abs_addr = true); State uretprobe_parser(); State usdt_parser(); State tracepoint_parser(); State profile_parser(); State interval_parser(); State software_parser(); State hardware_parser(); State watchpoint_parser(bool async = false); State fentry_parser(); State iter_parser(); State raw_tracepoint_parser(); State argument_count_error(int expected, std::optional expected2 = std::nullopt); std::optional stoull(const std::string &str); std::optional stoll(const std::string &str); ASTContext &ctx_; BPFtrace &bpftrace_; std::ostream &sink_; AttachPoint *ap_{ nullptr }; // Non-owning pointer std::stringstream errs_; std::vector parts_; AttachPointList new_attach_points; bool listing_; }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/codegen_helper.cpp000066400000000000000000000027641477746507000204430ustar00rootroot00000000000000#include "codegen_helper.h" namespace bpftrace::ast { // needAssignMapStatementAllocation determines if a map assignment requires a // new memory allocation. This happens only in a few cases e.g. if there are // two map assignments of tuples of different sizes e.g. // @x = ("xxx", 1); @x = ("xxxxxxx", 1); // which requires a 0 memsetting and copying of each tuple element into the // new allocation before calling bpf_map_update_elem. // // Another case when we need an allocation is for a external struct e.g. // $v = (struct task_struct *)arg1; @ = *$v;. // // Most cases we can reuse existing BPF memory and not create a new allocation. // // Note this function does NOT determine if an allocation should use scratch // buffer or the stack, that logic is in // IRBuilderBPF::CreateWriteMapValueAllocation bool needAssignMapStatementAllocation(const AssignMapStatement &assignment) { const auto &map = *assignment.map; const auto &expr_type = assignment.expr->type; if (shouldBeInBpfMemoryAlready(expr_type)) { return !expr_type.IsSameSizeRecursive(map.type); } else if (map.type.IsRecordTy() || map.type.IsArrayTy()) { return !expr_type.is_internal; } return true; } bool needMapKeyAllocation(const Map &map) { return needMapKeyAllocation(map, map.key_expr); } bool needMapKeyAllocation(const Map &map, Expression *key_expr) { if (key_expr && inBpfMemory(key_expr->type)) { return !key_expr->type.IsSameSizeRecursive(map.key_type); } return true; } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/codegen_helper.h000066400000000000000000000023611477746507000201010ustar00rootroot00000000000000#pragma once #include "bpftrace.h" namespace bpftrace { namespace ast { inline bool needMemcpy(const SizedType &stype) { return stype.IsAggregate() || stype.IsTimestampTy() || stype.IsCgroupPathTy(); } // BPF memory is memory that the program can access with a regular // dereference. This could mean the value is on the stack, a map, or // maybe something else (like BPF arenas) in the future. // // This means that a bpf_probe_read_*() is _NOT_ required. inline bool shouldBeInBpfMemoryAlready(const SizedType &type) { return type.IsStringTy() || type.IsBufferTy() || type.IsInetTy() || type.IsUsymTy() || type.IsKstackTy() || type.IsUstackTy() || type.IsTupleTy() || type.IsTimestampTy() || type.IsMacAddressTy() || type.IsCgroupPathTy(); } inline bool inBpfMemory(const SizedType &type) { return type.is_internal || shouldBeInBpfMemoryAlready(type); } inline AddrSpace find_addrspace_stack(const SizedType &ty) { return (shouldBeInBpfMemoryAlready(ty)) ? AddrSpace::kernel : ty.GetAS(); } bool needAssignMapStatementAllocation(const AssignMapStatement &assignment); bool needMapKeyAllocation(const Map &map); bool needMapKeyAllocation(const Map &map, Expression *key_expr); } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/dibuilderbpf.cpp000066400000000000000000000332501477746507000201250ustar00rootroot00000000000000#include "dibuilderbpf.h" #include #include #include "libbpf/bpf.h" #include "log.h" #include "struct.h" #include "utils.h" namespace bpftrace::ast { DIBuilderBPF::DIBuilderBPF(Module &module) : DIBuilder(module) { file = createFile("bpftrace.bpf.o", "."); } void DIBuilderBPF::createFunctionDebugInfo(llvm::Function &func, const SizedType &ret_type, const Struct &args, bool is_declaration) { // Return type should be at index 0 SmallVector types; types.reserve(args.fields.size() + 1); types.push_back(GetType(ret_type, false)); for (auto &arg : args.fields) types.push_back(GetType(arg.type, false)); DISubroutineType *ditype = createSubroutineType(getOrCreateTypeArray(types)); std::string sanitised_name = sanitise_bpf_program_name(func.getName().str()); DISubprogram::DISPFlags flags = DISubprogram::SPFlagZero; if (!is_declaration) flags |= DISubprogram::SPFlagDefinition; if (func.isLocalLinkage(func.getLinkage())) flags |= DISubprogram::DISPFlags::SPFlagLocalToUnit; DISubprogram *subprog = createFunction(file, sanitised_name, sanitised_name, file, 0, ditype, 0, DINode::FlagPrototyped, flags); #if LLVM_VERSION_MAJOR < 17 // There's a bug in LLVM <17 in DIBuilder::createFunction when called for a // function declaration. It creates an empty temporary MDTuple for // RetainedNodes inside DISubprogram which is, in addition, never freed. // // We generate function declaration debug info for kfuncs so this causes // two issues when kfuncs are used on LLVM <17: // // 1. The generated LLVM IR is invalid and its verification will fail. // 2. There is a memory leak introduced by the above createFunction call. // // To fix both problems, delete the temporary MDTuple here. // // Note that the issue was fixed in LLVM 17 by // // https://github.com/llvm/llvm-project/commit/ed506dd6cecd9653cf9202bfe195891a33482852 // // which removes the creation of the temporary MDTuple. // if (is_declaration) { llvm::MDNode::deleteTemporary(subprog->getRetainedNodes().get()); } #endif for (size_t i = 0; i < args.fields.size(); i++) { createParameterVariable(subprog, args.fields.at(i).name, i + 1, file, 0, static_cast(types[i + 1]), true); } func.setSubprogram(subprog); } void DIBuilderBPF::createProbeDebugInfo(llvm::Function &probe_func) { // BPF probe function has: // - int return type // - single parameter (ctx) of a pointer type Struct args; args.AddField("ctx", CreatePointer(CreateInt8())); createFunctionDebugInfo(probe_func, CreateInt64(), args); } DIType *DIBuilderBPF::getInt8Ty() { if (!types_.int8) types_.int8 = createBasicType("int8", 8, dwarf::DW_ATE_signed); return types_.int8; } DIType *DIBuilderBPF::getInt16Ty() { if (!types_.int16) types_.int16 = createBasicType("int16", 16, dwarf::DW_ATE_signed); return types_.int16; } DIType *DIBuilderBPF::getInt32Ty() { if (!types_.int32) types_.int32 = createBasicType("int32", 32, dwarf::DW_ATE_signed); return types_.int32; } DIType *DIBuilderBPF::getInt64Ty() { if (!types_.int64) types_.int64 = createBasicType("int64", 64, dwarf::DW_ATE_signed); return types_.int64; } DIType *DIBuilderBPF::getIntTy() { if (!types_.int_) types_.int_ = createBasicType("int", 32, dwarf::DW_ATE_signed); return types_.int_; } DIType *DIBuilderBPF::getInt8PtrTy() { if (!types_.int8_ptr) types_.int8_ptr = createPointerType(getInt8Ty(), 64); return types_.int8_ptr; } // Create anonymous struct with anonymous fields. It's possible that there will // be multiple tuples of the same (duplicated) type but BTF deduplication should // take care of that. DIType *DIBuilderBPF::CreateTupleType(const SizedType &stype) { assert(stype.IsTupleTy()); SmallVector fields; for (auto &field : stype.GetFields()) { fields.push_back(createMemberType(file, "", file, 0, field.type.GetSize() * 8, 0, field.offset * 8, DINode::FlagZero, GetType(field.type))); } DICompositeType *result = createStructType(file, "", file, 0, stype.GetSize() * 8, 0, DINode::FlagZero, nullptr, getOrCreateArray(fields)); return result; } DIType *DIBuilderBPF::CreateMapStructType(const SizedType &stype) { assert(stype.IsMinTy() || stype.IsMaxTy() || stype.IsAvgTy() || stype.IsStatsTy()); // For Min/Max, the first field is the value and the second field is the // "value is set" flag. For Avg/Stats, the first field is the total and the // second field is the count. SmallVector fields = { createMemberType(file, "", file, 0, stype.GetSize() * 8, 0, 0, DINode::FlagZero, getInt64Ty()), createMemberType(file, "", file, 0, stype.GetSize() * 8, 0, stype.GetSize() * 8, DINode::FlagZero, getInt32Ty()) }; DICompositeType *result = createStructType(file, "", file, 0, (stype.GetSize() * 8) * 2, 0, DINode::FlagZero, nullptr, getOrCreateArray(fields)); return result; } DIType *DIBuilderBPF::CreateByteArrayType(uint64_t num_bytes) { auto subrange = getOrCreateSubrange(0, num_bytes); return createArrayType( num_bytes * 8, 0, getInt8Ty(), getOrCreateArray({ subrange })); } /// Convert internal SizedType to a corresponding DIType type. /// /// In codegen, some types are not converted into a directly corresponding /// LLVM type but instead into a type which is easy to work with in BPF /// programs (see IRBuilderBPF::GetType for details). /// /// We do the same here for debug types and, similarly to IRBuilderBPF::GetType, /// allow to emit directly corresponding types by setting `emit_codegen_types` /// to false. This is necessary when emitting info for types whose BTF must /// exactly match the kernel BTF (e.g. kernel functions ("kfunc") prototypes). /// /// Note: IRBuilderBPF::GetType doesn't implement creating actual struct types /// as it is not necessary for the current use-cases. For debug info types, this /// is not the case and we need to emit a struct type with at least the correct /// name and size (fields are not necessary). DIType *DIBuilderBPF::GetType(const SizedType &stype, bool emit_codegen_types) { if (!emit_codegen_types && stype.IsRecordTy()) { std::string name = stype.GetName(); static constexpr std::string_view struct_prefix = "struct "; static constexpr std::string_view union_prefix = "union "; if (name.find(struct_prefix) == 0) name = name.substr(struct_prefix.length()); else if (name.find(union_prefix) == 0) name = name.substr(union_prefix.length()); return createStructType(file, name, file, 0, stype.GetSize() * 8, 0, DINode::FlagZero, nullptr, getOrCreateArray({})); } if (stype.IsByteArray() || stype.IsRecordTy() || stype.IsStack()) { auto subrange = getOrCreateSubrange(0, stype.GetSize()); return createArrayType( stype.GetSize() * 8, 0, getInt8Ty(), getOrCreateArray({ subrange })); } if (stype.IsArrayTy()) { auto subrange = getOrCreateSubrange(0, stype.GetNumElements()); return createArrayType(stype.GetSize() * 8, 0, GetType(*stype.GetElementTy()), getOrCreateArray({ subrange })); } if (stype.IsTupleTy()) return CreateTupleType(stype); if (stype.IsMinTy() || stype.IsMaxTy() || stype.IsAvgTy() || stype.IsStatsTy()) return CreateMapStructType(stype); if (stype.IsPtrTy()) return emit_codegen_types ? getInt64Ty() : createPointerType(GetType(*stype.GetPointeeTy(), emit_codegen_types), 64); // Integer types and builtin types represented by integers switch (stype.GetSize()) { case 8: return getInt64Ty(); case 4: return getInt32Ty(); case 2: return getInt16Ty(); case 1: return getInt8Ty(); default: LOG(BUG) << "Cannot generate debug info for type " << typestr(stype.GetTy()) << " (" << stype.GetSize() << " is not a valid type size)"; return nullptr; } } DIType *DIBuilderBPF::GetMapKeyType(const SizedType &key_type, const SizedType &value_type, libbpf::bpf_map_type map_type) { // No-key maps use '0' as the key. // - BPF requires 4-byte keys for array maps // - bpftrace uses 8 bytes for the implicit '0' key in hash maps if (key_type.IsNoneTy()) return (map_type == libbpf::BPF_MAP_TYPE_PERCPU_ARRAY || map_type == libbpf::BPF_MAP_TYPE_ARRAY) ? getInt32Ty() : getInt64Ty(); // Some map types need an extra 8-byte key. if (value_type.IsHistTy() || value_type.IsLhistTy()) { uint64_t size = key_type.GetSize() + 8; return CreateByteArrayType(size); } return GetType(key_type); } DIType *DIBuilderBPF::GetMapFieldInt(int value) { // Integer fields of map entry are represented by 64-bit pointers to an array // of int, in which dimensionality of the array encodes the specified value. auto subrange = getOrCreateSubrange(0, value); auto array = createArrayType( 32 * value, 0, getIntTy(), getOrCreateArray({ subrange })); return createPointerType(array, 64); } DIType *DIBuilderBPF::createPointerMemberType(const std::string &name, uint64_t offset, DIType *type) { return createMemberType( file, name, file, 0, 64, 0, offset, DINode::FlagZero, type); } DIGlobalVariableExpression *DIBuilderBPF::createMapEntry( const std::string &name, libbpf::bpf_map_type map_type, uint64_t max_entries, DIType *key_type, const SizedType &value_type) { SmallVector fields = { createPointerMemberType("type", 0, GetMapFieldInt(map_type)), createPointerMemberType("max_entries", 64, GetMapFieldInt(max_entries)), }; uint64_t size = 128; if (!value_type.IsNoneTy()) { fields.push_back( createPointerMemberType("key", size, createPointerType(key_type, 64))); fields.push_back(createPointerMemberType( "value", size + 64, createPointerType(GetType(value_type), 64))); size += 128; } DIType *map_entry_type = createStructType(file, "", file, 0, size, 0, DINode::FlagZero, nullptr, getOrCreateArray(fields)); return createGlobalVariableExpression( file, name, "global", file, 0, map_entry_type, false); } DIGlobalVariableExpression *DIBuilderBPF::createGlobalVariable( std::string_view name, const SizedType &stype) { return createGlobalVariableExpression( file, name, "global", file, 0, GetType(stype, false), false); } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/dibuilderbpf.h000066400000000000000000000045251477746507000175750ustar00rootroot00000000000000#pragma once #include "functions.h" #include "types.h" #include namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf #include namespace bpftrace { namespace ast { using namespace llvm; class DIBuilderBPF : public DIBuilder { public: DIBuilderBPF(Module &module); void createFunctionDebugInfo(llvm::Function &func, const SizedType &ret_type, const Struct &args, bool is_declaration = false); void createProbeDebugInfo(llvm::Function &probe_func); DIType *getInt8Ty(); DIType *getInt16Ty(); DIType *getInt32Ty(); DIType *getInt64Ty(); DIType *getInt8PtrTy(); // We need a separate type called "int" to mimic libbpf's behaviour of // generating debuginfo for some BPF map fields. For details, see comment in // DIBuilderBPF::GetMapFieldInt. DIType *getIntTy(); DIType *GetType(const SizedType &stype, bool emit_codegen_types = true); DIType *CreateTupleType(const SizedType &stype); DIType *CreateMapStructType(const SizedType &stype); DIType *CreateByteArrayType(uint64_t num_bytes); DIType *createPointerMemberType(const std::string &name, uint64_t offset, DIType *type); DIType *GetMapKeyType(const SizedType &key_type, const SizedType &value_type, libbpf::bpf_map_type map_type); DIType *GetMapFieldInt(int value); DIGlobalVariableExpression *createMapEntry(const std::string &name, libbpf::bpf_map_type map_type, uint64_t max_entries, DIType *key_type, const SizedType &value_type); DIGlobalVariableExpression *createGlobalVariable(std::string_view name, const SizedType &stype); DIFile *file = nullptr; private: struct { DIType *int8 = nullptr; DIType *int16 = nullptr; DIType *int32 = nullptr; DIType *int64 = nullptr; DIType *int128 = nullptr; DIType *int8_ptr = nullptr; DIType *int_ = nullptr; } types_; std::unordered_map structs_; }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/int_parser.cpp000066400000000000000000000071101477746507000176340ustar00rootroot00000000000000#include "ast/int_parser.h" #include #include #include #include #include #include #include namespace { template T _parse_int(const std::string &num __attribute__((unused)), size_t *idx __attribute__((unused)), int base __attribute__((unused))) { static_assert(not std::is_same_v, "BUG: _parse_int not implemented for type"); } template <> int64_t _parse_int(const std::string &num, size_t *idx, int base) { return std::stoll(num, idx, base); } template <> uint64_t _parse_int(const std::string &num, size_t *idx, int base) { return std::stoull(num, idx, base); } template std::variant _parse_int(const std::string &num, int base) { // https://en.cppreference.com/w/cpp/language/integer_literal#The_type_of_the_literal static auto int_size_re = std::regex("^(u|u?l?l)$", std::regex::icase); try { std::size_t idx; T ret = _parse_int(num, &idx, base); if (idx != num.size()) { auto trail = num.substr(idx, std::string::npos); auto match = std::regex_match(trail, int_size_re); if (!match) return "Found trailing non-numeric characters"; } return ret; } catch (const std::exception &ex) { return ex.what(); } } // integer variant of 10^exp uint64_t _ten_pow(uint64_t exp) { static const uint64_t v[] = { 1, 10, 100, 1000, 10000, 100000, 1000000 }; if (exp > 6) return v[6] * _ten_pow(exp - 6); return v[exp]; } // integer variant of scientific notation parsing template std::variant _parse_exp(const std::string &coeff, const std::string &exp) { std::stringstream errmsg; auto maybe_coeff = _parse_int(coeff, 10); if (std::string *err = std::get_if(&maybe_coeff)) { errmsg << "Coefficient part of scientific literal is not a valid number: " << coeff << ": " << *err; return errmsg.str(); } auto maybe_exp = _parse_int(exp, 10); if (std::string *err = std::get_if(&maybe_exp)) { errmsg << "Exponent part of scientific literal is not a valid number: " << exp << ": " << *err; return errmsg.str(); } auto c = std::get(maybe_coeff); auto e = std::get(maybe_exp); if (c > 9) { errmsg << "Coefficient part of scientific literal must be in range (0,9), " "got: " << coeff; return errmsg.str(); } if (e > 16) { errmsg << "Exponent will overflow integer range: " << exp; return errmsg.str(); } return c * static_cast(_ten_pow(e)); } } // namespace namespace bpftrace::ast::int_parser { template T to_int(const std::string &num, int base) { std::string n(num); n.erase(std::remove(n.begin(), n.end(), '_'), n.end()); std::variant res; // If hex if ((n.rfind("0x", 0) == 0) || (n.rfind("0X", 0) == 0)) { res = _parse_int(n, base); } else { auto pos = n.find_first_of("eE"); if (pos != std::string::npos) { res = _parse_exp(n.substr(0, pos), n.substr(pos + 1, std::string::npos)); } else { res = _parse_int(n, base); } } if (std::string *err = std::get_if(&res)) throw std::invalid_argument(*err); return std::get(res); } int64_t to_int(const std::string &num, int base) { return to_int(num, base); } uint64_t to_uint(const std::string &num, int base) { return to_int(num, base); } } // namespace bpftrace::ast::int_parser bpftrace-0.23.2/src/ast/int_parser.h000066400000000000000000000011511477746507000173000ustar00rootroot00000000000000#include #include namespace bpftrace { namespace ast { namespace int_parser { // String -> int conversion specific to bpftrace // // - error when trailing characters are found // - supports scientific notation, e.g. 1e6 // - error when out of int range (1e20) // - error when base > 9 (12e3) // - support underscore as separator, e.g. 1_234_000 // // All errors are raised as std::invalid_argument exception int64_t to_int(const std::string &num, int base); uint64_t to_uint(const std::string &num, int base); } // namespace int_parser } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/irbuilderbpf.cpp000066400000000000000000003230571477746507000201520ustar00rootroot00000000000000#include "ast/irbuilderbpf.h" #include #include #include #include #include #include "arch/arch.h" #include "ast/async_event_types.h" #include "ast/codegen_helper.h" #include "bpfmap.h" #include "bpftrace.h" #include "globalvars.h" #include "log.h" #include "utils.h" namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace::ast { namespace { std::string probeReadHelperName(libbpf::bpf_func_id id) { switch (id) { case libbpf::BPF_FUNC_probe_read: return "probe_read"; case libbpf::BPF_FUNC_probe_read_user: return "probe_read_user"; case libbpf::BPF_FUNC_probe_read_kernel: return "probe_read_kernel"; case libbpf::BPF_FUNC_probe_read_str: return "probe_read_str"; case libbpf::BPF_FUNC_probe_read_user_str: return "probe_read_user_str"; case libbpf::BPF_FUNC_probe_read_kernel_str: return "probe_read_kernel_str"; default: LOG(BUG) << "unknown probe_read id: " << std::to_string(id); } } } // namespace libbpf::bpf_func_id IRBuilderBPF::selectProbeReadHelper(AddrSpace as, bool str) { libbpf::bpf_func_id fn; // Assume that if a kernel has probe_read_kernel it has the other 3 too if (bpftrace_.feature_->has_helper_probe_read_kernel()) { if (as == AddrSpace::kernel) { fn = str ? libbpf::BPF_FUNC_probe_read_kernel_str : libbpf::BPF_FUNC_probe_read_kernel; } else if (as == AddrSpace::user) { fn = str ? libbpf::BPF_FUNC_probe_read_user_str : libbpf::BPF_FUNC_probe_read_user; } else { // if the kernel has the new helpers but AS is still none it is a bug // in bpftrace, assert catches it for debug builds. // assert(as != AddrSpace::none); static bool warnonce = false; if (!warnonce) { warnonce = true; LOG(WARNING) << "Addrspace is not set"; } fn = str ? libbpf::BPF_FUNC_probe_read_str : libbpf::BPF_FUNC_probe_read; } } else { fn = str ? libbpf::BPF_FUNC_probe_read_str : libbpf::BPF_FUNC_probe_read; } return fn; } // This constant is defined in the Linux kernel's proc_ns.h // It represents the inode of the initial (global) PID namespace constexpr uint32_t PROC_PID_INIT_INO = 0xeffffffc; Value *IRBuilderBPF::CreateGetPid(Value *ctx, const location &loc) { const auto &pidns = bpftrace_.get_pidns_self_stat(); if (pidns.st_ino != PROC_PID_INIT_INO) { // Get namespaced target PID when we're running in a namespace AllocaInst *res = CreateAllocaBPF(BpfPidnsInfoType(), "bpf_pidns_info"); CreateGetNsPidTgid( ctx, getInt64(pidns.st_dev), getInt64(pidns.st_ino), res, loc); Value *pid = CreateLoad( getInt32Ty(), CreateGEP(BpfPidnsInfoType(), res, { getInt32(0), getInt32(0) })); CreateLifetimeEnd(res); return pid; } // Get global target PID when we're in the initial namespace Value *pidtgid = CreateGetPidTgid(loc); Value *pid = CreateTrunc(CreateLShr(pidtgid, 32), getInt32Ty(), "pid"); return pid; } Value *IRBuilderBPF::CreateGetTid(Value *ctx, const location &loc) { const auto &pidns = bpftrace_.get_pidns_self_stat(); if (pidns.st_ino != PROC_PID_INIT_INO) { // Get namespaced target TID when we're running in a namespace AllocaInst *res = CreateAllocaBPF(BpfPidnsInfoType(), "bpf_pidns_info"); CreateGetNsPidTgid( ctx, getInt64(pidns.st_dev), getInt64(pidns.st_ino), res, loc); Value *tid = CreateLoad( getInt32Ty(), CreateGEP(BpfPidnsInfoType(), res, { getInt32(0), getInt32(1) })); CreateLifetimeEnd(res); return tid; } // Get global target TID when we're in the initial namespace Value *pidtgid = CreateGetPidTgid(loc); Value *tid = CreateTrunc(pidtgid, getInt32Ty(), "tid"); return tid; } AllocaInst *IRBuilderBPF::CreateUSym(Value *ctx, Value *val, int probe_id, const location &loc) { std::vector elements = { getInt64Ty(), // addr getInt32Ty(), // pid getInt32Ty(), // probe id }; StructType *usym_t = GetStructType("usym_t", elements, false); AllocaInst *buf = CreateAllocaBPF(usym_t, "usym"); Value *pid = CreateGetPid(ctx, loc); Value *probe_id_val = Constant::getIntegerValue(getInt32Ty(), APInt(32, probe_id)); // The extra 0 here ensures the type of addr_offset will be int64 Value *addr_offset = CreateGEP(usym_t, buf, { getInt64(0), getInt32(0) }); Value *pid_offset = CreateGEP(usym_t, buf, { getInt64(0), getInt32(1) }); Value *probeid_offset = CreateGEP(usym_t, buf, { getInt64(0), getInt32(2) }); CreateStore(val, addr_offset); CreateStore(pid, pid_offset); CreateStore(probe_id_val, probeid_offset); return buf; } StructType *IRBuilderBPF::GetStackStructType(bool is_ustack) { // Kernel stacks should not be differentiated by pid, since the kernel // address space is the same between pids (and when aggregating you *want* // to be able to correlate between pids in most cases). User-space stacks // are special because of ASLR, hence we also store the pid; probe id is // stored for cases when only ELF resolution works (e.g. ASLR disabled and // process exited). if (is_ustack) { std::vector elements{ getInt64Ty(), // stack id getInt64Ty(), // nr_stack_frames getInt32Ty(), // pid getInt32Ty(), // probe id }; return GetStructType("ustack_key", elements, false); } else { std::vector elements{ getInt64Ty(), // stack id getInt64Ty(), // nr_stack_frames }; return GetStructType("kstack_key", elements, false); } } StructType *IRBuilderBPF::GetStructType( std::string name, const std::vector &elements, bool packed) { auto search = structs_.find(name); if (search != structs_.end()) return search->second; StructType *s = StructType::create(elements, name, packed); structs_.insert({ name, s }); return s; } IRBuilderBPF::IRBuilderBPF(LLVMContext &context, Module &module, BPFtrace &bpftrace, AsyncIds &async_ids) : IRBuilder<>(context), module_(module), bpftrace_(bpftrace), async_ids_(async_ids) { // Declare external LLVM function FunctionType *pseudo_func_type = FunctionType::get( getInt64Ty(), { getInt64Ty(), getInt64Ty() }, false); llvm::Function::Create(pseudo_func_type, GlobalValue::ExternalLinkage, "llvm.bpf.pseudo", &module_); } void IRBuilderBPF::hoist(const std::function &functor) { llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock &entry_block = parent->getEntryBlock(); auto ip = saveIP(); if (entry_block.empty()) SetInsertPoint(&entry_block); else SetInsertPoint(&entry_block.front()); functor(); restoreIP(ip); } AllocaInst *IRBuilderBPF::CreateAllocaBPF(llvm::Type *ty, const std::string &name) { // Anything this large should be allocated in a scratch map instead assert(module_.getDataLayout().getTypeAllocSize(ty) <= 256); AllocaInst *alloca; hoist([this, ty, &name, &alloca]() { alloca = CreateAlloca(ty, nullptr, name); }); CreateLifetimeStart(alloca); return alloca; } AllocaInst *IRBuilderBPF::CreateAllocaBPF(const SizedType &stype, const std::string &name) { llvm::Type *ty = GetType(stype); return CreateAllocaBPF(ty, name); } void IRBuilderBPF::CreateAllocationInit(const SizedType &stype, Value *alloc) { if (needMemcpy(stype)) { CreateMemsetBPF(alloc, getInt8(0), stype.GetSize()); } else { CreateStore(ConstantInt::get(GetType(stype), 0), alloc); } } AllocaInst *IRBuilderBPF::CreateAllocaBPFInit(const SizedType &stype, const std::string &name) { // Anything this large should be allocated in a scratch map instead assert(stype.GetSize() <= 256); AllocaInst *alloca; hoist([this, &stype, &name, &alloca]() { llvm::Type *ty = GetType(stype); alloca = CreateAlloca(ty, nullptr, name); CreateLifetimeStart(alloca); CreateAllocationInit(stype, alloca); }); return alloca; } AllocaInst *IRBuilderBPF::CreateAllocaBPF(int bytes, const std::string &name) { llvm::Type *ty = ArrayType::get(getInt8Ty(), bytes); return CreateAllocaBPF(ty, name); } void IRBuilderBPF::CreateMemsetBPF(Value *ptr, Value *val, uint32_t size) { if (size > 512 && bpftrace_.feature_->has_helper_probe_read_kernel()) { // Note we are "abusing" bpf_probe_read_kernel() by reading from NULL // which triggers a call into the kernel-optimized memset(). // // Upstream blesses this trick so we should be able to count on them // to maintain these semantics. // // Also note we are avoiding a call to CreateProbeRead(), as it wraps // calls to probe read helpers with the -k error reporting feature. // The call here will always fail and we want it that way. So avoid // reporting errors to the user. auto probe_read_id = libbpf::BPF_FUNC_probe_read_kernel; FunctionType *proberead_func_type = FunctionType::get( getInt64Ty(), { ptr->getType(), getInt32Ty(), GetNull()->getType() }, false); PointerType *proberead_func_ptr_type = PointerType::get(proberead_func_type, 0); Constant *proberead_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64(probe_read_id), proberead_func_ptr_type); createCall(proberead_func_type, proberead_func, { ptr, getInt32(size), GetNull() }, probeReadHelperName(probe_read_id)); } else { // Use unrolled memset for memsets less than 512 bytes mostly for // correctness. // // It appears that helper based memsets obscure LLVM stack optimizer view // into memory usage such that programs that were below stack limit with // builtin memsets will bloat with helper based memsets enough to where // LLVM BPF backend will barf. // // So only use helper based memset when we really need it. And that's when // we're memset()ing off-stack. We know it's off stack b/c 512 is program // stack limit. CreateMemSet(ptr, val, getInt64(size), MaybeAlign(1)); } } void IRBuilderBPF::CreateMemcpyBPF(Value *dst, Value *src, uint32_t size) { if (size > 512 && bpftrace_.feature_->has_helper_probe_read_kernel()) { // Note we are avoiding a call to CreateProbeRead(), as it wraps // calls to probe read helpers with the -k error reporting feature. // // Errors are not ever expected, as memcpy should only be used when // you're sure src and dst are both in BPF memory. auto probe_read_id = libbpf::BPF_FUNC_probe_read_kernel; FunctionType *probe_read_func_type = FunctionType::get( getInt64Ty(), { dst->getType(), getInt32Ty(), src->getType() }, false); PointerType *probe_read_func_ptr_type = PointerType::get( probe_read_func_type, 0); Constant *probe_read_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64(probe_read_id), probe_read_func_ptr_type); createCall(probe_read_func_type, probe_read_func, { dst, getInt32(size), src }, probeReadHelperName(probe_read_id)); } else { CreateMemCpy(dst, MaybeAlign(1), src, MaybeAlign(1), size); } } llvm::ConstantInt *IRBuilderBPF::GetIntSameSize(uint64_t C, llvm::Type *ty) { assert(ty->isIntegerTy()); unsigned size = ty->getIntegerBitWidth(); return getIntN(size, C); } llvm::ConstantInt *IRBuilderBPF::GetIntSameSize(uint64_t C, llvm::Value *expr) { unsigned size = expr->getType()->getIntegerBitWidth(); return getIntN(size, C); } /// Convert internal SizedType to a corresponding LLVM type. /// /// For convenience, some types are not converted into a directly corresponding /// type but instead into a type which is easy to work with in BPF programs /// (e.g. store it in maps, etc.). This is the case for two particular types: /// - pointers are represented as i64 /// - structs (records) are represented as byte arrays. /// /// Setting `emit_codegen_types` to false (it is true by default) will change /// this behaviour and emit the exact corresponding types. This is typically /// necessary when creating a type which must exactly match the type in the /// kernel BTF (e.g. a kernel function (kfunc) prototype). /// /// At the moment, `emit_codegen_types=false` only applies to pointers as it is /// sufficient for our use cases (and we don't need to bother with emitting /// struct types with all the fields). This should be changed eventually. llvm::Type *IRBuilderBPF::GetType(const SizedType &stype, bool emit_codegen_types) { llvm::Type *ty; if (stype.IsByteArray() || stype.IsRecordTy()) { ty = ArrayType::get(getInt8Ty(), stype.GetSize()); } else if (stype.IsArrayTy()) { ty = ArrayType::get(GetType(*stype.GetElementTy()), stype.GetNumElements()); } else if (stype.IsTupleTy()) { std::vector llvm_elems; std::ostringstream ty_name; for (const auto &elem : stype.GetFields()) { auto &elemtype = elem.type; llvm_elems.emplace_back(GetType(elemtype)); ty_name << elemtype << "_"; } ty_name << "_tuple_t"; ty = GetStructType(ty_name.str(), llvm_elems, false); } else if (stype.IsStack()) { ty = GetStackStructType(stype.IsUstackTy()); } else if (stype.IsPtrTy()) { if (emit_codegen_types) ty = getInt64Ty(); else ty = getPtrTy(); } else if (stype.IsVoidTy()) { ty = getVoidTy(); } else { switch (stype.GetSize()) { case 16: ty = getInt128Ty(); break; case 8: ty = getInt64Ty(); break; case 4: ty = getInt32Ty(); break; case 2: ty = getInt16Ty(); break; case 1: ty = getInt8Ty(); break; default: LOG(BUG) << stype.GetSize() << " is not a valid type size for GetType"; } } return ty; } llvm::Type *IRBuilderBPF::GetMapValueType(const SizedType &stype) { llvm::Type *ty; if (stype.IsMinTy() || stype.IsMaxTy()) { // The first field is the value // The second field is the "value is set" flag std::vector llvm_elems = { getInt64Ty(), getInt64Ty() }; ty = GetStructType("min_max_val", llvm_elems, false); } else if (stype.IsAvgTy() || stype.IsStatsTy()) { // The first field is the total value // The second is the count value std::vector llvm_elems = { getInt64Ty(), getInt64Ty() }; ty = GetStructType("avg_stas_val", llvm_elems, false); } else { ty = GetType(stype); } return ty; } CallInst *IRBuilderBPF::CreateHelperCall(libbpf::bpf_func_id func_id, FunctionType *helper_type, ArrayRef args, const Twine &Name, const location *loc) { if (loc && bpftrace_.helper_use_loc_.find(func_id) == bpftrace_.helper_use_loc_.end()) bpftrace_.helper_use_loc_[func_id] = *loc; PointerType *helper_ptr_type = PointerType::get(helper_type, 0); Constant *helper_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64(func_id), helper_ptr_type); return createCall(helper_type, helper_func, args, Name); } CallInst *IRBuilderBPF::createCall(FunctionType *callee_type, Value *callee, ArrayRef args, const Twine &Name) { return CreateCall(callee_type, callee, args, Name); } Value *IRBuilderBPF::GetMapVar(const std::string &map_name) { return module_.getGlobalVariable(bpf_map_name(map_name)); } Value *IRBuilderBPF::GetNull() { return ConstantExpr::getCast(Instruction::IntToPtr, getInt64(0), getPtrTy()); } CallInst *IRBuilderBPF::CreateMapLookup(Map &map, Value *key, const std::string &name) { return createMapLookup(map.ident, key, name); } CallInst *IRBuilderBPF::createMapLookup(const std::string &map_name, Value *key, const std::string &name) { Value *map_ptr = GetMapVar(map_name); // void *map_lookup_elem(struct bpf_map * map, void * key) // Return: Map value or NULL assert(key->getType()->isPointerTy()); FunctionType *lookup_func_type = FunctionType::get( getPtrTy(), { map_ptr->getType(), key->getType() }, false); PointerType *lookup_func_ptr_type = PointerType::get(lookup_func_type, 0); Constant *lookup_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_map_lookup_elem), lookup_func_ptr_type); return createCall(lookup_func_type, lookup_func, { map_ptr, key }, name); } CallInst *IRBuilderBPF::createPerCpuMapLookup(const std::string &map_name, Value *key, Value *cpu, const std::string &name) { Value *map_ptr = GetMapVar(map_name); // void *map_lookup_percpu_elem(struct bpf_map * map, void * key, u32 cpu) // Return: Map value or NULL assert(key->getType()->isPointerTy()); FunctionType *lookup_func_type = FunctionType::get( getPtrTy(), { map_ptr->getType(), key->getType(), getInt32Ty() }, false); PointerType *lookup_func_ptr_type = PointerType::get(lookup_func_type, 0); Constant *lookup_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_map_lookup_percpu_elem), lookup_func_ptr_type); return createCall(lookup_func_type, lookup_func, { map_ptr, key, cpu }, name); } CallInst *IRBuilderBPF::CreateGetJoinMap(BasicBlock *failure_callback, const location &loc) { return createGetScratchMap( to_string(MapType::Join), "join", loc, failure_callback); } CallInst *IRBuilderBPF::CreateGetStackScratchMap(StackType stack_type, BasicBlock *failure_callback, const location &loc) { SizedType value_type = CreateArray(stack_type.limit, CreateUInt64()); return createGetScratchMap(StackType::scratch_name(), StackType::scratch_name(), loc, failure_callback); } Value *IRBuilderBPF::CreateGetStrAllocation(const std::string &name, const location &loc) { const auto max_strlen = bpftrace_.config_.get(ConfigKeyInt::max_strlen); const auto str_type = CreateArray(max_strlen, CreateInt8()); return createAllocation(bpftrace::globalvars::GlobalVar::GET_STR_BUFFER, GetType(str_type), name, loc, [](AsyncIds &async_ids) { return async_ids.str(); }); } Value *IRBuilderBPF::CreateGetFmtStringArgsAllocation(StructType *struct_type, const std::string &name, const location &loc) { return createAllocation(bpftrace::globalvars::GlobalVar::FMT_STRINGS_BUFFER, struct_type, name, loc); } Value *IRBuilderBPF::CreateTupleAllocation(const SizedType &tuple_type, const std::string &name, const location &loc) { return createAllocation(bpftrace::globalvars::GlobalVar::TUPLE_BUFFER, GetType(tuple_type), name, loc, [](AsyncIds &async_ids) { return async_ids.tuple(); }); } Value *IRBuilderBPF::CreateReadMapValueAllocation(const SizedType &value_type, const std::string &name, const location &loc) { return createAllocation( bpftrace::globalvars::GlobalVar::READ_MAP_VALUE_BUFFER, GetType(value_type), name, loc, [](AsyncIds &async_ids) { return async_ids.read_map_value(); }); } Value *IRBuilderBPF::CreateWriteMapValueAllocation(const SizedType &value_type, const std::string &name, const location &loc) { return createAllocation( bpftrace::globalvars::GlobalVar::WRITE_MAP_VALUE_BUFFER, GetType(value_type), name, loc); } Value *IRBuilderBPF::CreateVariableAllocationInit(const SizedType &value_type, const std::string &name, const location &loc) { // Hoist variable declaration and initialization to entry point of // probe/subprogram. While we technically do not need this as variables // are properly scoped, it eases debugging and is consistent with previous // stack-only variable implementation. Value *alloc; hoist([this, &value_type, &name, &loc, &alloc] { alloc = createAllocation(bpftrace::globalvars::GlobalVar::VARIABLE_BUFFER, GetType(value_type), name, loc, [](AsyncIds &async_ids) { return async_ids.variable(); }); CreateAllocationInit(value_type, alloc); }); return alloc; } Value *IRBuilderBPF::CreateMapKeyAllocation(const SizedType &value_type, const std::string &name, const location &loc) { return createAllocation(bpftrace::globalvars::GlobalVar::MAP_KEY_BUFFER, GetType(value_type), name, loc, [](AsyncIds &async_ids) { return async_ids.map_key(); }); } Value *IRBuilderBPF::createAllocation( bpftrace::globalvars::GlobalVar globalvar, llvm::Type *obj_type, const std::string &name, const location &loc, std::optional> gen_async_id_cb) { const auto obj_size = module_.getDataLayout().getTypeAllocSize(obj_type); const auto on_stack_limit = bpftrace_.config_.get( ConfigKeyInt::on_stack_limit); if (obj_size > on_stack_limit) { return createScratchBuffer( globalvar, loc, gen_async_id_cb ? (*gen_async_id_cb)(async_ids_) : 0); } return CreateAllocaBPF(obj_type, name); } Value *IRBuilderBPF::createScratchBuffer( bpftrace::globalvars::GlobalVar globalvar, const location &loc, size_t key) { auto config = globalvars::get_config(globalvar); // ValueType var[MAX_CPU_ID + 1][num_elements] auto type = globalvars::get_type(globalvar, bpftrace_.resources, bpftrace_.config_); // Get CPU ID auto cpu_id = CreateGetCpuId(loc); auto max = CreateLoad(getInt64Ty(), module_.getGlobalVariable(to_string( bpftrace::globalvars::GlobalVar::MAX_CPU_ID))); // Mask CPU ID by MAX_CPU_ID to ensure BPF verifier knows CPU ID is bounded // on older kernels. Note this means MAX_CPU_ID must be 2^N - 1 for some N. // See get_max_cpu_id() for more details. auto bounded_cpu_id = CreateAnd(cpu_id, max, "cpu.id.bounded"); // Note the 1st index is 0 because we're pointing to // ValueType var[MAX_CPU_ID + 1][num_elements] // 2nd/3rd/4th indexes actually index into the array // See https://llvm.org/docs/LangRef.html#id236 return CreateGEP(GetType(type), module_.getGlobalVariable(config.name), { getInt64(0), bounded_cpu_id, getInt64(key), getInt64(0) }); } // Failure to lookup a scratch map will result in a jump to the // failure_callback, if non-null. // // In practice, a properly constructed percpu lookup will never fail. The only // way it can fail is if we have a bug in our code. So a null failure_callback // simply causes a blind 0 return. See comment in function for why this is ok. CallInst *IRBuilderBPF::createGetScratchMap(const std::string &map_name, const std::string &name, const location &loc, BasicBlock *failure_callback, int key) { AllocaInst *keyAlloc = CreateAllocaBPF(getInt32Ty(), "lookup_" + name + "_key"); CreateStore(getInt32(key), keyAlloc); CallInst *call = createMapLookup(map_name, keyAlloc, "lookup_" + name + "_map"); CreateLifetimeEnd(keyAlloc); llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *lookup_failure_block = BasicBlock::Create( module_.getContext(), "lookup_" + name + "_failure", parent); BasicBlock *lookup_merge_block = BasicBlock::Create( module_.getContext(), "lookup_" + name + "_merge", parent); Value *condition = CreateICmpNE(CreateIntCast(call, getPtrTy(), true), GetNull(), "lookup_" + name + "_cond"); CreateCondBr(condition, lookup_merge_block, lookup_failure_block); SetInsertPoint(lookup_failure_block); CreateDebugOutput("unable to find the scratch map value for " + name, std::vector{}, loc); if (failure_callback) { CreateBr(failure_callback); } else { // Think of this like an assert(). In practice, we cannot fail to lookup a // percpu array map unless we have a coding error. Rather than have some // kind of complicated fallback path where we provide an error string for // our caller, just indicate to verifier we want to terminate execution. // // Note that we blindly return 0 in contrast to the logic inside // CodegenLLVM::createRet(). That's b/c the return value doesn't matter // if it'll never get executed. CreateRet(getInt64(0)); } SetInsertPoint(lookup_merge_block); return call; } Value *IRBuilderBPF::CreateMapLookupElem(Value *ctx, Map &map, Value *key, const location &loc) { assert(ctx && ctx->getType() == getPtrTy()); return CreateMapLookupElem(ctx, map.ident, key, map.type, loc); } Value *IRBuilderBPF::CreateMapLookupElem(Value *ctx, const std::string &map_name, Value *key, SizedType &type, const location &loc) { assert(ctx && ctx->getType() == getPtrTy()); CallInst *call = createMapLookup(map_name, key); // Check if result == 0 llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *lookup_success_block = BasicBlock::Create(module_.getContext(), "lookup_success", parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_.getContext(), "lookup_failure", parent); BasicBlock *lookup_merge_block = BasicBlock::Create(module_.getContext(), "lookup_merge", parent); Value *value = CreateReadMapValueAllocation(type, "lookup_elem_val", loc); Value *condition = CreateICmpNE(CreateIntCast(call, getPtrTy(), true), GetNull(), "map_lookup_cond"); CreateCondBr(condition, lookup_success_block, lookup_failure_block); SetInsertPoint(lookup_success_block); if (needMemcpy(type)) CreateMemcpyBPF(value, call, type.GetSize()); else { assert(GetType(type) == getInt64Ty()); CreateStore(CreateLoad(getInt64Ty(), call), value); } CreateBr(lookup_merge_block); SetInsertPoint(lookup_failure_block); if (needMemcpy(type)) CreateMemsetBPF(value, getInt8(0), type.GetSize()); else CreateStore(getInt64(0), value); CreateHelperError(ctx, getInt32(0), libbpf::BPF_FUNC_map_lookup_elem, loc); CreateBr(lookup_merge_block); SetInsertPoint(lookup_merge_block); if (needMemcpy(type)) return value; // value is a pointer to i64 Value *ret = CreateLoad(getInt64Ty(), value); if (dyn_cast(value)) CreateLifetimeEnd(value); return ret; } Value *IRBuilderBPF::CreatePerCpuMapAggElems(Value *ctx, Map &map, Value *key, const SizedType &type, const location &loc) { // int ret = 0; // int i = 0; // while (i < nr_cpus) { // int * cpu_value = map_lookup_percpu_elem(map, key, i); // if (cpu_value == NULL) { // if (i == 0) // log_error("Key not found") // else // debug("No cpu found for cpu id: %lu", i) // Mostly for AOT // break; // } // // update ret for sum, count, avg, min, max // i++; // } // return ret; assert(ctx && ctx->getType() == getPtrTy()); const std::string &map_name = map.ident; AllocaInst *i = CreateAllocaBPF(getInt32Ty(), "i"); AllocaInst *val_1 = CreateAllocaBPF(getInt64Ty(), "val_1"); // used for min/max/avg AllocaInst *val_2 = CreateAllocaBPF(getInt64Ty(), "val_2"); CreateStore(getInt32(0), i); CreateStore(getInt64(0), val_1); CreateStore(getInt64(0), val_2); llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *while_cond = BasicBlock::Create(module_.getContext(), "while_cond", parent); BasicBlock *while_body = BasicBlock::Create(module_.getContext(), "while_body", parent); BasicBlock *while_end = BasicBlock::Create(module_.getContext(), "while_end", parent); CreateBr(while_cond); SetInsertPoint(while_cond); auto *cond = CreateICmp( CmpInst::ICMP_ULT, CreateLoad(getInt32Ty(), i), CreateLoad(getInt32Ty(), module_.getGlobalVariable( to_string(bpftrace::globalvars::GlobalVar::NUM_CPUS))), "num_cpu.cmp"); CreateCondBr(cond, while_body, while_end); SetInsertPoint(while_body); CallInst *call = createPerCpuMapLookup(map_name, key, CreateLoad(getInt32Ty(), i)); llvm::Function *lookup_parent = GetInsertBlock()->getParent(); BasicBlock *lookup_success_block = BasicBlock::Create(module_.getContext(), "lookup_success", lookup_parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_.getContext(), "lookup_failure", lookup_parent); Value *condition = CreateICmpNE(CreateIntCast(call, getPtrTy(), true), GetNull(), "map_lookup_cond"); CreateCondBr(condition, lookup_success_block, lookup_failure_block); SetInsertPoint(lookup_success_block); if (type.IsMinTy() || type.IsMaxTy()) { createPerCpuMinMax(val_1, val_2, call, type); } else if (type.IsAvgTy()) { createPerCpuAvg(val_1, val_2, call, type); } else if (type.IsSumTy() || type.IsCountTy()) { createPerCpuSum(val_1, call, type); } else { LOG(BUG) << "Unsupported map aggregation type: " << type; } // ++i; CreateStore(CreateAdd(CreateLoad(getInt32Ty(), i), getInt32(1)), i); CreateBr(while_cond); SetInsertPoint(lookup_failure_block); llvm::Function *error_parent = GetInsertBlock()->getParent(); BasicBlock *error_success_block = BasicBlock::Create(module_.getContext(), "error_success", error_parent); BasicBlock *error_failure_block = BasicBlock::Create(module_.getContext(), "error_failure", error_parent); // If the CPU is 0 and the map lookup fails it means the key doesn't exist Value *error_condition = CreateICmpEQ(CreateLoad(getInt32Ty(), i), getInt32(0), "error_lookup_cond"); CreateCondBr(error_condition, error_success_block, error_failure_block); SetInsertPoint(error_success_block); CreateHelperError( ctx, getInt32(0), libbpf::BPF_FUNC_map_lookup_percpu_elem, loc); CreateBr(while_end); SetInsertPoint(error_failure_block); // This should only get triggered in the AOT case CreateDebugOutput("No cpu found for cpu id: %lu", std::vector{ CreateLoad(getInt32Ty(), i) }, loc); CreateBr(while_end); SetInsertPoint(while_end); CreateLifetimeEnd(i); Value *ret_reg; if (type.IsAvgTy()) { AllocaInst *ret = CreateAllocaBPF(getInt64Ty(), "ret"); // BPF doesn't yet support a signed division so we have to check if // the value is negative, flip it, do an unsigned division, and then // flip it back if (type.IsSigned()) { llvm::Function *avg_parent = GetInsertBlock()->getParent(); BasicBlock *is_negative_block = BasicBlock::Create(module_.getContext(), "is_negative", avg_parent); BasicBlock *is_positive_block = BasicBlock::Create(module_.getContext(), "is_positive", avg_parent); BasicBlock *merge_block = BasicBlock::Create(module_.getContext(), "is_negative_merge_block", avg_parent); Value *is_negative_condition = CreateICmpSLT( CreateLoad(getInt64Ty(), val_1), getInt64(0), "is_negative_cond"); CreateCondBr(is_negative_condition, is_negative_block, is_positive_block); SetInsertPoint(is_negative_block); Value *pos_total = CreateAdd(CreateNot(CreateLoad(getInt64Ty(), val_1)), getInt64(1)); Value *pos_avg = CreateUDiv(pos_total, CreateLoad(getInt64Ty(), val_2)); CreateStore(CreateNeg(pos_avg), ret); CreateBr(merge_block); SetInsertPoint(is_positive_block); CreateStore(CreateUDiv(CreateLoad(getInt64Ty(), val_1), CreateLoad(getInt64Ty(), val_2)), ret); CreateBr(merge_block); SetInsertPoint(merge_block); ret_reg = CreateLoad(getInt64Ty(), ret); CreateLifetimeEnd(ret); } else { ret_reg = CreateUDiv(CreateLoad(getInt64Ty(), val_1), CreateLoad(getInt64Ty(), val_2)); } } else { ret_reg = CreateLoad(getInt64Ty(), val_1); } CreateLifetimeEnd(val_1); CreateLifetimeEnd(val_2); return ret_reg; } void IRBuilderBPF::createPerCpuSum(AllocaInst *ret, CallInst *call, const SizedType &type) { CreateStore(CreateAdd(CreateLoad(GetType(type), call), CreateLoad(getInt64Ty(), ret)), ret); } void IRBuilderBPF::createPerCpuMinMax(AllocaInst *ret, AllocaInst *is_ret_set, CallInst *call, const SizedType &type) { auto *value_type = GetMapValueType(type); bool is_max = type.IsMaxTy(); Value *mm_val = CreateLoad( getInt64Ty(), CreateGEP(value_type, call, { getInt64(0), getInt32(0) })); Value *is_val_set = CreateLoad( getInt64Ty(), CreateGEP(value_type, call, { getInt64(0), getInt32(1) })); // (ret, is_ret_set, min_max_val, is_val_set) { // // if the min_max_val is 0, which is the initial map value, // // we need to know if it was explicitly set by user // if (!is_val_set == 1) { // return; // } // if (!is_ret_set == 1) { // ret = min_max_val; // is_ret_set = 1; // } else if (min_max_val > ret) { // or min_max_val < ret if min operation // ret = min_max_val; // is_ret_set = 1; // } llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *val_set_success = BasicBlock::Create(module_.getContext(), "val_set_success", parent); BasicBlock *min_max_success = BasicBlock::Create(module_.getContext(), "min_max_success", parent); BasicBlock *ret_set_success = BasicBlock::Create(module_.getContext(), "ret_set_success", parent); BasicBlock *merge_block = BasicBlock::Create(module_.getContext(), "min_max_merge", parent); Value *val_set_condition = CreateICmpEQ(is_val_set, getInt64(1), "val_set_cond"); Value *ret_set_condition = CreateICmpEQ(CreateLoad(getInt64Ty(), is_ret_set), getInt64(1), "ret_set_cond"); Value *min_max_condition; if (is_max) { min_max_condition = type.IsSigned() ? CreateICmpSGT(mm_val, CreateLoad(getInt64Ty(), ret), "max_cond") : CreateICmpUGT(mm_val, CreateLoad(getInt64Ty(), ret), "max_cond"); } else { min_max_condition = type.IsSigned() ? CreateICmpSLT(mm_val, CreateLoad(getInt64Ty(), ret), "min_cond") : CreateICmpULT(mm_val, CreateLoad(getInt64Ty(), ret), "max_cond"); } // if (is_val_set == 1) CreateCondBr(val_set_condition, val_set_success, merge_block); SetInsertPoint(val_set_success); // if (is_ret_set == 1) CreateCondBr(ret_set_condition, ret_set_success, min_max_success); SetInsertPoint(ret_set_success); // if (min_max_val > ret) or if (min_max_val < ret) CreateCondBr(min_max_condition, min_max_success, merge_block); SetInsertPoint(min_max_success); // ret = cpu_value; CreateStore(mm_val, ret); // is_ret_set = 1; CreateStore(getInt64(1), is_ret_set); CreateBr(merge_block); SetInsertPoint(merge_block); } void IRBuilderBPF::createPerCpuAvg(AllocaInst *total, AllocaInst *count, CallInst *call, const SizedType &type) { auto *value_type = GetMapValueType(type); Value *total_val = CreateLoad( getInt64Ty(), CreateGEP(value_type, call, { getInt64(0), getInt32(0) })); Value *count_val = CreateLoad( getInt64Ty(), CreateGEP(value_type, call, { getInt64(0), getInt32(1) })); CreateStore(CreateAdd(total_val, CreateLoad(getInt64Ty(), total)), total); CreateStore(CreateAdd(count_val, CreateLoad(getInt64Ty(), count)), count); } void IRBuilderBPF::CreateMapUpdateElem(Value *ctx, const std::string &map_ident, Value *key, Value *val, const location &loc, int64_t flags) { Value *map_ptr = GetMapVar(map_ident); assert(ctx && ctx->getType() == getPtrTy()); assert(key->getType()->isPointerTy()); assert(val->getType()->isPointerTy()); Value *flags_val = getInt64(flags); // long map_update_elem(struct bpf_map * map, void *key, void * value, u64 // flags) Return: 0 on success or negative error FunctionType *update_func_type = FunctionType::get( getInt64Ty(), { map_ptr->getType(), key->getType(), val->getType(), getInt64Ty() }, false); PointerType *update_func_ptr_type = PointerType::get(update_func_type, 0); Constant *update_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_map_update_elem), update_func_ptr_type); CallInst *call = createCall(update_func_type, update_func, { map_ptr, key, val, flags_val }, "update_elem"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_map_update_elem, loc); } void IRBuilderBPF::CreateMapDeleteElem(Value *ctx, Map &map, Value *key, const location &loc) { assert(ctx && ctx->getType() == getPtrTy()); assert(key->getType()->isPointerTy()); Value *map_ptr = GetMapVar(map.ident); // long map_delete_elem(&map, &key) // Return: 0 on success or negative error FunctionType *delete_func_type = FunctionType::get( getInt64Ty(), { map_ptr->getType(), key->getType() }, false); PointerType *delete_func_ptr_type = PointerType::get(delete_func_type, 0); Constant *delete_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_map_delete_elem), delete_func_ptr_type); CallInst *call = createCall( delete_func_type, delete_func, { map_ptr, key }, "delete_elem"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_map_delete_elem, loc); } Value *IRBuilderBPF::CreateForEachMapElem(Value *ctx, Map &map, Value *callback, Value *callback_ctx, const location &loc) { Value *map_ptr = GetMapVar(map.ident); // long bpf_for_each_map_elem(struct bpf_map *map, void *callback_fn, void // *callback_ctx, u64 flags) // // Return: 0 on success or negative error // // callback is long (*callback_fn)(struct bpf_map *map, const void *key, void // *value, void *ctx); FunctionType *for_each_map_type = FunctionType::get( getInt64Ty(), { map_ptr->getType(), callback->getType(), getPtrTy(), getInt64Ty() }, false); PointerType *for_each_map_ptr_type = PointerType::get(for_each_map_type, 0); Constant *for_each_map_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_for_each_map_elem), for_each_map_ptr_type); CallInst *call = createCall( for_each_map_type, for_each_map_func, { map_ptr, callback, callback_ctx ? CreateIntToPtr(callback_ctx, getPtrTy()) : GetNull(), /*flags=*/getInt64(0) }, "for_each_map_elem"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_for_each_map_elem, loc); return call; } void IRBuilderBPF::CreateCheckSetRecursion(const location &loc, int early_exit_ret) { const std::string map_ident = to_string(MapType::RecursionPrevention); AllocaInst *key = CreateAllocaBPF(getInt32Ty(), "lookup_key"); CreateStore(getInt32(0), key); CallInst *call = createMapLookup(map_ident, key); llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *lookup_success_block = BasicBlock::Create(module_.getContext(), "lookup_success", parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_.getContext(), "lookup_failure", parent); BasicBlock *merge_block = BasicBlock::Create(module_.getContext(), "lookup_merge", parent); // Make the verifier happy with a null check even though the value should // never be null for key 0. Value *condition = CreateICmpNE( CreateIntCast(call, getPtrTy(), true), ConstantExpr::getCast(Instruction::IntToPtr, getInt64(0), getPtrTy()), "map_lookup_cond"); CreateCondBr(condition, lookup_success_block, lookup_failure_block); SetInsertPoint(lookup_success_block); CreateLifetimeEnd(key); // createMapLookup returns an u8* auto *cast = CreatePtrToInt(call, getInt64Ty(), "cast"); Value *prev_value = CREATE_ATOMIC_RMW(AtomicRMWInst::BinOp::Xchg, cast, getInt64(1), 8, AtomicOrdering::SequentiallyConsistent); llvm::Function *set_parent = GetInsertBlock()->getParent(); BasicBlock *value_is_set_block = BasicBlock::Create(module_.getContext(), "value_is_set", set_parent); Value *set_condition = CreateICmpEQ(prev_value, getInt64(0), "value_set_condition"); CreateCondBr(set_condition, merge_block, value_is_set_block); SetInsertPoint(value_is_set_block); // The counter is set, we need to exit early from the probe. // Most of the time this will happen for the functions that can lead // to a crash e.g. "queued_spin_lock_slowpath" but it can also happen // for nested probes e.g. "page_fault_user" -> "print". CreateAtomicIncCounter(to_string(MapType::EventLossCounter), bpftrace_.event_loss_cnt_key_); CreateRet(getInt64(early_exit_ret)); SetInsertPoint(lookup_failure_block); CreateDebugOutput( "Value for per-cpu map key 0 is null. This shouldn't happen.", std::vector{}, loc); CreateRet(getInt64(0)); SetInsertPoint(merge_block); } void IRBuilderBPF::CreateUnSetRecursion(const location &loc) { const std::string map_ident = to_string(MapType::RecursionPrevention); AllocaInst *key = CreateAllocaBPF(getInt32Ty(), "lookup_key"); CreateStore(getInt32(0), key); CallInst *call = createMapLookup(map_ident, key); llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *lookup_success_block = BasicBlock::Create(module_.getContext(), "lookup_success", parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_.getContext(), "lookup_failure", parent); BasicBlock *merge_block = BasicBlock::Create(module_.getContext(), "lookup_merge", parent); // Make the verifier happy with a null check even though the value should // never be null for key 0. Value *condition = CreateICmpNE( CreateIntCast(call, getPtrTy(), true), ConstantExpr::getCast(Instruction::IntToPtr, getInt64(0), getPtrTy()), "map_lookup_cond"); CreateCondBr(condition, lookup_success_block, lookup_failure_block); SetInsertPoint(lookup_success_block); CreateLifetimeEnd(key); // createMapLookup returns an u8* auto *cast = CreatePtrToInt(call, getInt64Ty(), "cast"); CreateStore(getInt64(0), cast); CreateBr(merge_block); SetInsertPoint(lookup_failure_block); CreateDebugOutput( "Value for per-cpu map key 0 is null. This shouldn't happen.", std::vector{}, loc); CreateBr(merge_block); SetInsertPoint(merge_block); } void IRBuilderBPF::CreateProbeRead(Value *ctx, Value *dst, llvm::Value *size, Value *src, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getPtrTy()); assert(size && size->getType()->getIntegerBitWidth() <= 32); size = CreateIntCast(size, getInt32Ty(), false); // int bpf_probe_read(void *dst, int size, void *src) // Return: 0 on success or negative error auto read_fn = selectProbeReadHelper(as, false); FunctionType *proberead_func_type = FunctionType::get( getInt64Ty(), { dst->getType(), getInt32Ty(), src->getType() }, false); PointerType *proberead_func_ptr_type = PointerType::get(proberead_func_type, 0); Constant *proberead_func = ConstantExpr::getCast(Instruction::IntToPtr, getInt64(read_fn), proberead_func_ptr_type); CallInst *call = createCall(proberead_func_type, proberead_func, { dst, size, src }, probeReadHelperName(read_fn)); CreateHelperErrorCond(ctx, call, read_fn, loc); } CallInst *IRBuilderBPF::CreateProbeReadStr(Value *ctx, Value *dst, size_t size, Value *src, AddrSpace as, const location &loc) { return CreateProbeReadStr(ctx, dst, getInt32(size), src, as, loc); } CallInst *IRBuilderBPF::CreateProbeReadStr(Value *ctx, Value *dst, llvm::Value *size, Value *src, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getPtrTy()); assert(size && size->getType()->isIntegerTy()); if ([[maybe_unused]] auto *dst_alloca = dyn_cast(dst)) { assert(dst_alloca->getAllocatedType()->isArrayTy() && dst_alloca->getAllocatedType()->getArrayElementType() == getInt8Ty()); } auto *size_i32 = size; if (size_i32->getType()->getScalarSizeInBits() != 32) size_i32 = CreateIntCast(size_i32, getInt32Ty(), false); auto read_fn = selectProbeReadHelper(as, true); // int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr) FunctionType *probereadstr_func_type = FunctionType::get( getInt64Ty(), { dst->getType(), getInt32Ty(), src->getType() }, false); PointerType *probereadstr_func_ptr_type = PointerType::get( probereadstr_func_type, 0); Constant *probereadstr_callee = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(read_fn), probereadstr_func_ptr_type); CallInst *call = createCall(probereadstr_func_type, probereadstr_callee, { dst, size_i32, src }, probeReadHelperName(read_fn)); CreateHelperErrorCond(ctx, call, read_fn, loc); return call; } Value *IRBuilderBPF::CreateUSDTReadArgument(Value *ctx, struct bcc_usdt_argument *argument, Builtin &builtin, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getPtrTy()); // Argument size must be 1, 2, 4, or 8. See // https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation int abs_size = std::abs(argument->size); assert(abs_size == 1 || abs_size == 2 || abs_size == 4 || abs_size == 8); if (argument->valid & BCC_USDT_ARGUMENT_DEREF_IDENT) LOG(ERROR) << "deref ident is not handled yet [" << argument->deref_ident << "]"; // USDT arguments can be any valid gas (GNU asm) operand. // BCC normalises these into the bcc_usdt_argument and supports most // valid gas operands. // // This code handles the following argument types: // * A constant (ARGUMENT_CONSTANT) // // * The value of a register (ARGUMENT_BASE_REGISTER_NAME without // ARGUMENT_DEREF_OFFSET set). // // * The value at address: base_register + offset + (index_register * scale) // Where index_register and scale are optional. // Note: Offset is optional in the gas operand, however will be set as zero // if the register needs to be dereferenced. if (argument->valid & BCC_USDT_ARGUMENT_CONSTANT) { // Correctly sign extend and convert to a 64-bit int return CreateIntCast(getIntN(abs_size * 8, argument->constant), getInt64Ty(), argument->size < 0); } if (argument->valid & BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME && !(argument->valid & BCC_USDT_ARGUMENT_BASE_REGISTER_NAME)) { // Invalid combination?? LOG(ERROR) << "index register set without base register;" << " this case is not yet handled"; } Value *result = nullptr; if (argument->valid & BCC_USDT_ARGUMENT_BASE_REGISTER_NAME) { int offset = 0; offset = arch::offset(argument->base_register_name); if (offset < 0) { LOG(BUG) << "offset for register " << argument->base_register_name << " not known"; } // 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 = CreateSafeGEP(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) { int ioffset = arch::offset(argument->index_register_name); if (ioffset < 0) { LOG(BUG) << "offset for register " << argument->index_register_name << " not known"; } index_offset = CreateSafeGEP(getInt8Ty(), ctx, getInt64(ioffset * sizeof(uintptr_t)), "load_register"); index_offset = CreateLoad(getInt64Ty(), index_offset); if (argument->valid & BCC_USDT_ARGUMENT_SCALE) { index_offset = CreateMul(index_offset, getInt64(argument->scale)); } } if (argument->valid & BCC_USDT_ARGUMENT_DEREF_OFFSET) { Value *ptr = CreateAdd(CreateLoad(getInt64Ty(), reg), getInt64(argument->deref_offset)); if (index_offset) { ptr = CreateAdd(ptr, index_offset); } CreateProbeRead(ctx, dst, getInt32(abs_size), ptr, as, loc); result = CreateLoad(getIntNTy(abs_size * 8), dst); } else { result = CreateLoad(getIntNTy(abs_size * 8), reg); } // Sign extend and convert to a bpftools standard 64-bit integer type result = CreateIntCast(result, getInt64Ty(), argument->size < 0); CreateLifetimeEnd(dst); } return result; } Value *IRBuilderBPF::CreateUSDTReadArgument(Value *ctx, AttachPoint *attach_point, int usdt_location_index, int arg_num, Builtin &builtin, pid_t pid, AddrSpace as, const location &loc) { assert(ctx && ctx->getType() == getPtrTy()); struct bcc_usdt_argument argument; void *usdt; if (pid) { // FIXME use attach_point->target when iovisor/bcc#2064 is merged usdt = bcc_usdt_new_frompid(pid, nullptr); } else { usdt = bcc_usdt_new_frompath(attach_point->target.c_str()); } if (usdt == nullptr) { LOG(ERROR) << "failed to initialize usdt context for probe " << attach_point->target; exit(-1); } std::string ns = attach_point->usdt.provider; std::string func = attach_point->usdt.name; if (bcc_usdt_get_argument(usdt, ns.c_str(), func.c_str(), usdt_location_index, arg_num, &argument) != 0) { LOG(ERROR) << "couldn't get argument " << arg_num << " for " << attach_point->target << ":" << attach_point->ns << ":" << attach_point->func; exit(-2); } Value *result = CreateUSDTReadArgument(ctx, &argument, builtin, as, loc); bcc_usdt_close(usdt); return result; } Value *IRBuilderBPF::CreateStrncmp(Value *str1, Value *str2, uint64_t n, bool inverse) { // This function compares each character of the two string. It returns true // if all are equal and false if any are different. // // strcmp(String val1, String val2) // { // for (size_t i = 0; i < n; i++) // { // // if (val1[i] != val2[i]) // { // return false; // } // if (val1[i] == NULL) // { // return true; // } // } // // return true; // } llvm::Function *parent = GetInsertBlock()->getParent(); AllocaInst *store = CreateAllocaBPF(getInt1Ty(), "strcmp.result"); BasicBlock *str_ne = BasicBlock::Create(module_.getContext(), "strcmp.false", parent); BasicBlock *done = BasicBlock::Create(module_.getContext(), "strcmp.done", parent); CreateStore(getInt1(!inverse), store); Value *null_byte = getInt8(0); for (size_t i = 0; i < n; i++) { BasicBlock *char_eq = BasicBlock::Create(module_.getContext(), "strcmp.loop", parent); BasicBlock *loop_null_check = BasicBlock::Create(module_.getContext(), "strcmp.loop_null_cmp", parent); Value *l; auto *ptr_l = CreateGEP(getInt8Ty(), str1, { getInt32(i) }); l = CreateLoad(getInt8Ty(), ptr_l); Value *r; auto *ptr_r = CreateGEP(getInt8Ty(), str2, { getInt32(i) }); r = CreateLoad(getInt8Ty(), ptr_r); Value *cmp = CreateICmpNE(l, r, "strcmp.cmp"); CreateCondBr(cmp, str_ne, loop_null_check); SetInsertPoint(loop_null_check); Value *cmp_null = CreateICmpEQ(l, null_byte, "strcmp.cmp_null"); CreateCondBr(cmp_null, done, char_eq); SetInsertPoint(char_eq); } CreateBr(done); SetInsertPoint(done); CreateStore(getInt1(inverse), store); CreateBr(str_ne); SetInsertPoint(str_ne); // store is a pointer to bool (i1 *) Value *result = CreateLoad(getInt1Ty(), store); CreateLifetimeEnd(store); result = CreateIntCast(result, getInt64Ty(), false); return result; } Value *IRBuilderBPF::CreateStrcontains(Value *haystack, uint64_t haystack_sz, Value *needle, uint64_t needle_sz) { // This function compares whether the string haystack contains the string // needle. It returns true if needle is contained by haystack, false if not // contained. // // clang-format off // // bool strcontains(char *haystack, size_t haystack_sz, char *needle, size_t needle_sz) { // // Explicit check needed for haystack="", needle="" case // if (needle[0] == '\0') // return true; // // for (u64 i = 0; i < haystack_sz && haystack[i] != '\0'; i++) { // u64 j; // for (j = 0; j < needle_sz; j++) { // if (needle[j] == '\0') // return true; // // if ((i+j) >= haystack_sz || haystack[i+j] != needle[j]) // break; // } // } // // return false; // } // // clang-format on llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *empty_check = BasicBlock::Create(module_.getContext(), "strcontains.empty.check", parent); BasicBlock *outer_cond = BasicBlock::Create(module_.getContext(), "strcontains.outer.cond", parent); BasicBlock *outer_cond_cmp = BasicBlock::Create(module_.getContext(), "strcontains.outer.cond.cmp", parent); BasicBlock *outer_body = BasicBlock::Create(module_.getContext(), "strcontains.outer.body", parent); BasicBlock *inner_cond = BasicBlock::Create(module_.getContext(), "strcontains.inner.cond", parent); BasicBlock *inner_body = BasicBlock::Create(module_.getContext(), "strcontains.inner.body", parent); BasicBlock *inner_notnull = BasicBlock::Create(module_.getContext(), "strcontains.inner.notnull", parent); BasicBlock *inner_cmp = BasicBlock::Create(module_.getContext(), "strcontains.inner.cmp", parent); BasicBlock *inner_incr = BasicBlock::Create(module_.getContext(), "strcontains.inner.incr", parent); BasicBlock *outer_incr = BasicBlock::Create(module_.getContext(), "strcontains.outer.incr", parent); BasicBlock *done_true = BasicBlock::Create(module_.getContext(), "strcontains.true", parent); BasicBlock *done_false = BasicBlock::Create(module_.getContext(), "strcontains.false", parent); BasicBlock *done = BasicBlock::Create(module_.getContext(), "strcontains.done", parent); AllocaInst *i = CreateAllocaBPF(getInt64Ty(), "strcontains.i"); CreateStore(getInt64(0), i); AllocaInst *j = CreateAllocaBPF(getInt64Ty(), "strcontains.j"); Value *null_byte = getInt8(0); CreateBr(empty_check); SetInsertPoint(empty_check); Value *needle_first = CreateLoad( getInt8Ty(), CreateGEP(getInt8Ty(), needle, { getInt64(0) })); Value *needle_first_null = CreateICmpEQ(needle_first, null_byte); CreateCondBr(needle_first_null, done_true, outer_cond); SetInsertPoint(outer_cond); Value *i_val = CreateLoad(getInt64Ty(), i); Value *haystack_inbounds = CreateICmpULT(i_val, getInt64(haystack_sz)); CreateCondBr(haystack_inbounds, outer_cond_cmp, done_false); SetInsertPoint(outer_cond_cmp); Value *haystack_char = CreateLoad( getInt8Ty(), CreateGEP(getInt8Ty(), haystack, { i_val })); Value *haystack_valid = CreateICmpNE(haystack_char, null_byte); CreateCondBr(haystack_valid, outer_body, done_false); SetInsertPoint(outer_body); CreateStore(getInt64(0), j); CreateBr(inner_cond); SetInsertPoint(inner_cond); Value *j_val = CreateLoad(getInt64Ty(), j); Value *needle_inbounds = CreateICmpULT(j_val, getInt64(needle_sz)); CreateCondBr(needle_inbounds, inner_body, outer_incr); SetInsertPoint(inner_body); Value *needle_char = CreateLoad(getInt8Ty(), CreateGEP(getInt8Ty(), needle, { j_val })); Value *needle_null = CreateICmpEQ(needle_char, null_byte); CreateCondBr(needle_null, done_true, inner_notnull); SetInsertPoint(inner_notnull); Value *haystack_cmp_idx = CreateAdd(i_val, j_val); Value *haystack_cmp_outbounds = CreateICmpUGE(haystack_cmp_idx, getInt64(haystack_sz)); CreateCondBr(haystack_cmp_outbounds, outer_incr, inner_cmp); // Inner conditional matching haystack char with needle char SetInsertPoint(inner_cmp); Value *haystack_cmp_char = CreateLoad( getInt8Ty(), CreateGEP(getInt8Ty(), haystack, { haystack_cmp_idx })); Value *haystack_cmp_needle = CreateICmpNE(haystack_cmp_char, needle_char); CreateCondBr(haystack_cmp_needle, outer_incr, inner_incr); SetInsertPoint(inner_incr); CreateStore(CreateAdd(CreateLoad(getInt64Ty(), j), getInt64(1)), j); CreateBr(inner_cond); SetInsertPoint(outer_incr); CreateStore(CreateAdd(CreateLoad(getInt64Ty(), i), getInt64(1)), i); CreateBr(outer_cond); SetInsertPoint(done_false); CreateBr(done); SetInsertPoint(done_true); CreateBr(done); SetInsertPoint(done); auto phi = CreatePHI(getInt1Ty(), 2, "result"); phi->addIncoming(getInt1(false), done_false); phi->addIncoming(getInt1(true), done_true); CreateLifetimeEnd(j); CreateLifetimeEnd(i); return CreateIntCast(phi, getInt64Ty(), false); } CallInst *IRBuilderBPF::CreateGetNs(TimestampMode ts, const location &loc) { // Random default value to silence compiler warning libbpf::bpf_func_id fn = libbpf::BPF_FUNC_ktime_get_ns; switch (ts) { case TimestampMode::monotonic: fn = libbpf::BPF_FUNC_ktime_get_ns; break; case TimestampMode::boot: fn = bpftrace_.feature_->has_helper_ktime_get_boot_ns() ? libbpf::BPF_FUNC_ktime_get_boot_ns : libbpf::BPF_FUNC_ktime_get_ns; break; case TimestampMode::tai: fn = libbpf::BPF_FUNC_ktime_get_tai_ns; break; case TimestampMode::sw_tai: LOG(BUG) << "Invalid timestamp mode: " << std::to_string( static_cast>(ts)); } // u64 ktime_get_*ns() // Return: current ktime FunctionType *gettime_func_type = FunctionType::get(getInt64Ty(), false); return CreateHelperCall(fn, gettime_func_type, {}, "get_ns", &loc); } CallInst *IRBuilderBPF::CreateJiffies64(const location &loc) { // u64 bpf_jiffies64() // Return: jiffies (BITS_PER_LONG == 64) or jiffies_64 (otherwise) FunctionType *jiffies64_func_type = FunctionType::get(getInt64Ty(), false); return CreateHelperCall( libbpf::BPF_FUNC_jiffies64, jiffies64_func_type, {}, "jiffies64", &loc); } Value *IRBuilderBPF::CreateIntegerArrayCmp(Value *ctx, Value *val1, Value *val2, const SizedType &val1_type, const SizedType &val2_type, const bool inverse, const location &loc, MDNode *metadata) { // This function compares each character of the two arrays. It returns true // if all are equal and false if any are different. // // cmp([]char val1, []char val2) // { // for (size_t i = 0; i < n; i++) // { // if (val1[i] != val2[i]) // { // return false; // } // } // return true; // } auto elem_type = *val1_type.GetElementTy(); const size_t num = val1_type.GetNumElements(); Value *val1_elem_i, *val2_elem_i, *cmp; AllocaInst *v1 = CreateAllocaBPF(elem_type, "v1"); AllocaInst *v2 = CreateAllocaBPF(elem_type, "v2"); AllocaInst *store = CreateAllocaBPF(getInt1Ty(), "arraycmp.result"); CreateStore(getInt1(inverse), store); llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *while_cond = BasicBlock::Create(module_.getContext(), "while_cond", parent); BasicBlock *while_body = BasicBlock::Create(module_.getContext(), "while_body", parent); BasicBlock *arr_ne = BasicBlock::Create(module_.getContext(), "arraycmp.false", parent); BasicBlock *done = BasicBlock::Create(module_.getContext(), "arraycmp.done", parent); Value *ptr_val1 = CreateIntToPtr(val1, getPtrTy()); Value *ptr_val2 = CreateIntToPtr(val2, getPtrTy()); AllocaInst *i = CreateAllocaBPF(getInt32Ty(), "i"); AllocaInst *n = CreateAllocaBPF(getInt32Ty(), "n"); CreateStore(getInt32(0), i); CreateStore(getInt32(num), n); CreateBr(while_cond); SetInsertPoint(while_cond); auto *cond = CreateICmpSLT(CreateLoad(getInt32Ty(), i), CreateLoad(getInt32Ty(), n), "size_check"); Instruction *loop_hdr = CreateCondBr(cond, while_body, done); loop_hdr->setMetadata(LLVMContext::MD_loop, metadata); SetInsertPoint(while_body); BasicBlock *arr_eq = BasicBlock::Create(module_.getContext(), "arraycmp.loop", parent); auto *ptr_val1_elem_i = CreateGEP(GetType(val1_type), ptr_val1, { getInt32(0), CreateLoad(getInt32Ty(), i) }); if (inBpfMemory(val1_type)) { val1_elem_i = CreateLoad(GetType(elem_type), ptr_val1_elem_i); } else { CreateProbeRead(ctx, v1, getInt32(elem_type.GetSize()), ptr_val1_elem_i, val1_type.GetAS(), loc); val1_elem_i = CreateLoad(GetType(elem_type), v1); } auto *ptr_val2_elem_i = CreateGEP(GetType(val2_type), ptr_val2, { getInt32(0), CreateLoad(getInt32Ty(), i) }); if (inBpfMemory(val2_type)) { val2_elem_i = CreateLoad(GetType(elem_type), ptr_val2_elem_i); } else { CreateProbeRead(ctx, v2, getInt32(elem_type.GetSize()), ptr_val2_elem_i, val2_type.GetAS(), loc); val2_elem_i = CreateLoad(GetType(elem_type), v2); } cmp = CreateICmpNE(val1_elem_i, val2_elem_i, "arraycmp.cmp"); CreateCondBr(cmp, arr_ne, arr_eq); SetInsertPoint(arr_eq); CreateStore(CreateAdd(CreateLoad(getInt32Ty(), i), getInt32(1)), i); CreateBr(while_cond); SetInsertPoint(arr_ne); CreateStore(getInt1(!inverse), store); CreateBr(done); SetInsertPoint(done); Value *result = CreateLoad(getInt1Ty(), store); CreateLifetimeEnd(store); CreateLifetimeEnd(v1); CreateLifetimeEnd(v2); result = CreateIntCast(result, getInt64Ty(), false); return result; } CallInst *IRBuilderBPF::CreateGetPidTgid(const location &loc) { // u64 bpf_get_current_pid_tgid(void) // Return: current->tgid << 32 | current->pid FunctionType *getpidtgid_func_type = FunctionType::get(getInt64Ty(), false); return CreateHelperCall(libbpf::BPF_FUNC_get_current_pid_tgid, getpidtgid_func_type, {}, "get_pid_tgid", &loc); } void IRBuilderBPF::CreateGetNsPidTgid(Value *ctx, Value *dev, Value *ino, AllocaInst *ret, const location &loc) { // long bpf_get_ns_current_pid_tgid( // u64 dev, u64 ino, struct bpf_pidns_info *nsdata, u32 size) // Return: 0 on success auto &layout = module_.getDataLayout(); auto struct_size = layout.getTypeAllocSize(BpfPidnsInfoType()); FunctionType *getnspidtgid_func_type = FunctionType::get(getInt64Ty(), { getInt64Ty(), getInt64Ty(), getPtrTy(), getInt32Ty(), }, false); CallInst *call = CreateHelperCall(libbpf::BPF_FUNC_get_ns_current_pid_tgid, getnspidtgid_func_type, { dev, ino, ret, getInt32(struct_size) }, "get_ns_pid_tgid", &loc); CreateHelperErrorCond( ctx, call, libbpf::BPF_FUNC_get_ns_current_pid_tgid, loc); } llvm::Type *IRBuilderBPF::BpfPidnsInfoType() { return GetStructType("bpf_pidns_info", { getInt32Ty(), getInt32Ty(), }, false); } CallInst *IRBuilderBPF::CreateGetCurrentCgroupId(const location &loc) { // u64 bpf_get_current_cgroup_id(void) // Return: 64-bit cgroup-v2 id FunctionType *getcgroupid_func_type = FunctionType::get(getInt64Ty(), false); return CreateHelperCall(libbpf::BPF_FUNC_get_current_cgroup_id, getcgroupid_func_type, {}, "get_cgroup_id", &loc); } CallInst *IRBuilderBPF::CreateGetUidGid(const location &loc) { // u64 bpf_get_current_uid_gid(void) // Return: current_gid << 32 | current_uid FunctionType *getuidgid_func_type = FunctionType::get(getInt64Ty(), false); return CreateHelperCall(libbpf::BPF_FUNC_get_current_uid_gid, getuidgid_func_type, {}, "get_uid_gid", &loc); } CallInst *IRBuilderBPF::CreateGetNumaId(const location &loc) { // long bpf_get_numa_node_id(void) // Return: NUMA Node ID FunctionType *numaid_func_type = FunctionType::get(getInt64Ty(), false); return CreateHelperCall(libbpf::BPF_FUNC_get_numa_node_id, numaid_func_type, {}, "get_numa_id", &loc); } CallInst *IRBuilderBPF::CreateGetCpuId(const location &loc) { // u32 bpf_raw_smp_processor_id(void) // Return: SMP processor ID FunctionType *getcpuid_func_type = FunctionType::get(getInt64Ty(), false); return CreateHelperCall(libbpf::BPF_FUNC_get_smp_processor_id, getcpuid_func_type, {}, "get_cpu_id", &loc); } CallInst *IRBuilderBPF::CreateGetCurrentTask(const location &loc) { // u64 bpf_get_current_task(void) // Return: current task_struct FunctionType *getcurtask_func_type = FunctionType::get(getInt64Ty(), false); return CreateHelperCall(libbpf::BPF_FUNC_get_current_task, getcurtask_func_type, {}, "get_cur_task", &loc); } CallInst *IRBuilderBPF::CreateGetRandom(const location &loc) { // u32 bpf_get_prandom_u32(void) // Return: random FunctionType *getrandom_func_type = FunctionType::get(getInt32Ty(), false); return CreateHelperCall(libbpf::BPF_FUNC_get_prandom_u32, getrandom_func_type, {}, "get_random", &loc); } CallInst *IRBuilderBPF::CreateGetStack(Value *ctx, bool ustack, Value *buf, StackType stack_type, const location &loc) { assert(ctx && ctx->getType() == getPtrTy()); int flags = 0; if (ustack) flags |= (1 << 8); Value *flags_val = getInt64(flags); Value *stack_size = getInt32(stack_type.limit * sizeof(uint64_t)); // long bpf_get_stack(void *ctx, void *buf, u32 size, u64 flags) // Return: The non-negative copied *buf* length equal to or less than // *size* on success, or a negative error in case of failure. FunctionType *getstack_func_type = FunctionType::get( getInt64Ty(), { getPtrTy(), getPtrTy(), getInt32Ty(), getInt64Ty() }, false); CallInst *call = CreateHelperCall(libbpf::BPF_FUNC_get_stack, getstack_func_type, { ctx, buf, stack_size, flags_val }, "get_stack", &loc); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_get_stack, loc); return call; } CallInst *IRBuilderBPF::CreateGetFuncIp(Value *ctx, const location &loc) { // u64 bpf_get_func_ip(void *ctx) // Return: // Address of the traced function for kprobe. // 0 for kprobes placed within the function (not at the entry). // Address of the probe for uprobe and return uprobe. FunctionType *getfuncip_func_type = FunctionType::get(getInt64Ty(), { getPtrTy() }, false); return CreateHelperCall(libbpf::BPF_FUNC_get_func_ip, getfuncip_func_type, { ctx }, "get_func_ip", &loc); } CallInst *IRBuilderBPF::CreatePerCpuPtr(Value *var, Value *cpu, const location &loc) { // void *bpf_per_cpu_ptr(const void *percpu_ptr, u32 cpu) // Return: // A pointer pointing to the kernel percpu variable on // cpu, or NULL, if cpu is invalid. FunctionType *percpuptr_func_type = FunctionType::get( getPtrTy(), { getPtrTy(), getInt64Ty() }, false); return CreateHelperCall(libbpf::BPF_FUNC_per_cpu_ptr, percpuptr_func_type, { var, cpu }, "per_cpu_ptr", &loc); } CallInst *IRBuilderBPF::CreateThisCpuPtr(Value *var, const location &loc) { // void *bpf_per_cpu_ptr(const void *percpu_ptr) // Return: // A pointer pointing to the kernel percpu variable on // this cpu. May never be NULL. FunctionType *percpuptr_func_type = FunctionType::get(getPtrTy(), { getPtrTy() }, false); return CreateHelperCall(libbpf::BPF_FUNC_this_cpu_ptr, percpuptr_func_type, { var }, "this_cpu_ptr", &loc); } void IRBuilderBPF::CreateGetCurrentComm(Value *ctx, AllocaInst *buf, size_t size, const location &loc) { assert(buf->getAllocatedType()->isArrayTy() && buf->getAllocatedType()->getArrayNumElements() >= size && buf->getAllocatedType()->getArrayElementType() == getInt8Ty()); // long bpf_get_current_comm(char *buf, int size_of_buf) // Return: 0 on success or negative error FunctionType *getcomm_func_type = FunctionType::get( getInt64Ty(), { buf->getType(), getInt64Ty() }, false); CallInst *call = CreateHelperCall(libbpf::BPF_FUNC_get_current_comm, getcomm_func_type, { buf, getInt64(size) }, "get_comm", &loc); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_get_current_comm, loc); } void IRBuilderBPF::CreateOutput(Value *ctx, Value *data, size_t size, const location *loc) { assert(ctx && ctx->getType() == getPtrTy()); assert(data && data->getType()->isPointerTy()); if (bpftrace_.feature_->has_map_ringbuf()) { CreateRingbufOutput(data, size, loc); } else { CreatePerfEventOutput(ctx, data, size, loc); } } void IRBuilderBPF::CreateRingbufOutput(Value *data, size_t size, const location *loc) { Value *map_ptr = GetMapVar(to_string(MapType::Ringbuf)); // long bpf_ringbuf_output(void *ringbuf, void *data, u64 size, u64 flags) FunctionType *ringbuf_output_func_type = FunctionType::get( getInt64Ty(), { map_ptr->getType(), data->getType(), getInt64Ty(), getInt64Ty() }, false); Value *ret = CreateHelperCall(libbpf::BPF_FUNC_ringbuf_output, ringbuf_output_func_type, { map_ptr, data, getInt64(size), getInt64(0) }, "ringbuf_output", loc); llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *loss_block = BasicBlock::Create(module_.getContext(), "event_loss_counter", parent); BasicBlock *merge_block = BasicBlock::Create(module_.getContext(), "counter_merge", parent); Value *condition = CreateICmpSLT(ret, getInt64(0), "ringbuf_loss"); CreateCondBr(condition, loss_block, merge_block); SetInsertPoint(loss_block); CreateAtomicIncCounter(to_string(MapType::EventLossCounter), bpftrace_.event_loss_cnt_key_); CreateBr(merge_block); SetInsertPoint(merge_block); } void IRBuilderBPF::CreateAtomicIncCounter(const std::string &map_name, uint32_t idx) { AllocaInst *key = CreateAllocaBPF(getInt32Ty(), "key"); CreateStore(getInt32(idx), key); CallInst *call = createMapLookup(map_name, key); llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *lookup_success_block = BasicBlock::Create(module_.getContext(), "lookup_success", parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_.getContext(), "lookup_failure", parent); BasicBlock *lookup_merge_block = BasicBlock::Create(module_.getContext(), "lookup_merge", parent); Value *condition = CreateICmpNE(CreateIntCast(call, getPtrTy(), true), GetNull(), "map_lookup_cond"); CreateCondBr(condition, lookup_success_block, lookup_failure_block); SetInsertPoint(lookup_success_block); CREATE_ATOMIC_RMW(AtomicRMWInst::BinOp::Add, call, getInt64(1), 8, AtomicOrdering::SequentiallyConsistent); CreateBr(lookup_merge_block); SetInsertPoint(lookup_failure_block); // ignore lookup failure CreateBr(lookup_merge_block); SetInsertPoint(lookup_merge_block); CreateLifetimeEnd(key); } void IRBuilderBPF::CreateMapElemInit(Value *ctx, Map &map, Value *key, Value *val, const location &loc) { AllocaInst *initValue = CreateAllocaBPF(val->getType(), "initial_value"); CreateStore(val, initValue); CreateMapUpdateElem(ctx, map.ident, key, initValue, loc, BPF_NOEXIST); CreateLifetimeEnd(initValue); return; } void IRBuilderBPF::CreateMapElemAdd(Value *ctx, Map &map, Value *key, Value *val, const location &loc) { CallInst *call = CreateMapLookup(map, key); SizedType &type = map.type; llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *lookup_success_block = BasicBlock::Create(module_.getContext(), "lookup_success", parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_.getContext(), "lookup_failure", parent); BasicBlock *lookup_merge_block = BasicBlock::Create(module_.getContext(), "lookup_merge", parent); AllocaInst *value = CreateAllocaBPF(type, "lookup_elem_val"); Value *condition = CreateICmpNE(CreateIntCast(call, getPtrTy(), true), GetNull(), "map_lookup_cond"); CreateCondBr(condition, lookup_success_block, lookup_failure_block); SetInsertPoint(lookup_success_block); // createMapLookup returns an u8* auto *cast = CreatePtrToInt(call, value->getType(), "cast"); CreateStore(CreateAdd(CreateLoad(value->getAllocatedType(), cast), val), cast); CreateBr(lookup_merge_block); SetInsertPoint(lookup_failure_block); CreateMapElemInit(ctx, map, key, val, loc); CreateBr(lookup_merge_block); SetInsertPoint(lookup_merge_block); CreateLifetimeEnd(value); return; } void IRBuilderBPF::CreatePerfEventOutput(Value *ctx, Value *data, size_t size, const location *loc) { Value *map_ptr = GetMapVar(to_string(MapType::PerfEvent)); Value *flags_val = getInt64(BPF_F_CURRENT_CPU); Value *size_val = getInt64(size); // long bpf_perf_event_output(struct pt_regs *ctx, struct bpf_map *map, // u64 flags, void *data, u64 size) FunctionType *perfoutput_func_type = FunctionType::get(getInt64Ty(), { getPtrTy(), map_ptr->getType(), getInt64Ty(), data->getType(), getInt64Ty() }, false); CreateHelperCall(libbpf::BPF_FUNC_perf_event_output, perfoutput_func_type, { ctx, map_ptr, flags_val, data, size_val }, "perf_event_output", loc); } void IRBuilderBPF::CreateDebugOutput(std::string fmt_str, const std::vector &values, const location &loc) { if (!bpftrace_.debug_output_) return; fmt_str = "[BPFTRACE_DEBUG_OUTPUT] " + fmt_str; Constant *const_str = ConstantDataArray::getString(module_.getContext(), fmt_str, true); AllocaInst *fmt = CreateAllocaBPF( ArrayType::get(getInt8Ty(), fmt_str.length() + 1), "fmt_str"); CreateMemsetBPF(fmt, getInt8(0), fmt_str.length() + 1); CreateStore(const_str, fmt); CreateTracePrintk(fmt, getInt32(fmt_str.length() + 1), values, loc); } void IRBuilderBPF::CreateTracePrintk(Value *fmt_ptr, Value *fmt_size, const std::vector &values, const location &loc) { std::vector args = { fmt_ptr, fmt_size }; for (auto val : values) { args.push_back(val); } // long bpf_trace_printk(const char *fmt, u32 fmt_size, ...) FunctionType *traceprintk_func_type = FunctionType::get( getInt64Ty(), { getPtrTy(), getInt32Ty() }, true); CreateHelperCall(libbpf::BPF_FUNC_trace_printk, traceprintk_func_type, args, "trace_printk", &loc); } void IRBuilderBPF::CreateSignal(Value *ctx, Value *sig, const location &loc) { // long bpf_send_signal(u32 sig) // Return: 0 or error FunctionType *signal_func_type = FunctionType::get(getInt64Ty(), { getInt32Ty() }, false); PointerType *signal_func_ptr_type = PointerType::get(signal_func_type, 0); Constant *signal_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_send_signal), signal_func_ptr_type); CallInst *call = createCall(signal_func_type, signal_func, { sig }, "signal"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_send_signal, loc); } void IRBuilderBPF::CreateOverrideReturn(Value *ctx, Value *rc) { // long bpf_override_return(struct pt_regs *regs, u64 rc) // Return: 0 FunctionType *override_func_type = FunctionType::get( getInt64Ty(), { getPtrTy(), getInt64Ty() }, false); PointerType *override_func_ptr_type = PointerType::get(override_func_type, 0); Constant *override_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_override_return), override_func_ptr_type); createCall(override_func_type, override_func, { ctx, rc }, "override"); } CallInst *IRBuilderBPF::CreateSkbOutput(Value *skb, Value *len, AllocaInst *data, size_t size) { Value *flags, *map_ptr, *size_val; map_ptr = GetMapVar(to_string(MapType::PerfEvent)); flags = len; flags = CreateShl(flags, 32); flags = CreateOr(flags, getInt64(BPF_F_CURRENT_CPU)); size_val = getInt64(size); // long bpf_skb_output(void *skb, struct bpf_map *map, u64 flags, // void *data, u64 size) FunctionType *skb_output_func_type = FunctionType::get(getInt64Ty(), { skb->getType(), map_ptr->getType(), getInt64Ty(), data->getType(), getInt64Ty() }, false); PointerType *skb_output_func_ptr_type = PointerType::get(skb_output_func_type, 0); Constant *skb_output_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_skb_output), skb_output_func_ptr_type); CallInst *call = createCall(skb_output_func_type, skb_output_func, { skb, map_ptr, flags, data, size_val }, "skb_output"); return call; } Value *IRBuilderBPF::CreateKFuncArg(Value *ctx, SizedType &type, std::string &name) { assert(type.IsIntTy() || type.IsPtrTy()); Value *expr = CreateLoad( GetType(type), CreateSafeGEP(getInt64Ty(), ctx, getInt64(type.funcarg_idx)), name); // LLVM 7.0 <= does not have CreateLoad(*Ty, *Ptr, isVolatile, Name), // so call setVolatile() manually dyn_cast(expr)->setVolatile(true); return expr; } Value *IRBuilderBPF::CreateRawTracepointArg(Value *ctx, const std::string &builtin) { // argX int offset = atoi(builtin.substr(3).c_str()); llvm::Type *type = getInt64Ty(); // All arguments are coerced into Int64. Value *expr = CreateLoad(type, CreateSafeGEP(type, ctx, getInt64(offset)), builtin); return expr; } Value *IRBuilderBPF::CreateUprobeArgsRecord(Value *ctx, const SizedType &args_type) { assert(args_type.IsRecordTy()); auto *args_t = UprobeArgsType(args_type); AllocaInst *result = CreateAllocaBPF(args_t, "args"); for (auto &arg : args_type.GetFields()) { assert(arg.type.is_funcarg); Value *arg_read = CreateRegisterRead( ctx, "arg" + std::to_string(arg.type.funcarg_idx)); if (arg.type.GetSize() != 8) arg_read = CreateTrunc(arg_read, GetType(arg.type)); CreateStore(arg_read, CreateGEP(args_t, result, { getInt64(0), getInt32(arg.type.funcarg_idx) })); } return result; } llvm::Type *IRBuilderBPF::UprobeArgsType(const SizedType &args_type) { auto type_name = args_type.GetName(); type_name.erase(0, strlen("struct ")); std::vector arg_types; for (auto &arg : args_type.GetFields()) arg_types.push_back(GetType(arg.type)); return GetStructType(type_name, arg_types, false); } Value *IRBuilderBPF::CreateRegisterRead(Value *ctx, const std::string &builtin) { int offset; if (builtin == "retval") offset = arch::ret_offset(); else if (builtin == "func") offset = arch::pc_offset(); else // argX offset = arch::arg_offset(atoi(builtin.substr(3).c_str())); return CreateRegisterRead(ctx, offset, builtin); } Value *IRBuilderBPF::CreateRegisterRead(Value *ctx, int offset, const std::string &name) { // Bitwidth of register values in struct pt_regs is the same as the kernel // pointer width on all supported architectures. llvm::Type *registerTy = getKernelPointerStorageTy(); Value *result = CreateLoad(registerTy, CreateSafeGEP(registerTy, ctx, getInt64(offset)), name); // LLVM 7.0 <= does not have CreateLoad(*Ty, *Ptr, isVolatile, Name), // so call setVolatile() manually dyn_cast(result)->setVolatile(true); // Caller expects an int64, so add a cast if the register size is different. if (result->getType()->getIntegerBitWidth() != 64) { result = CreateIntCast(result, getInt64Ty(), false); } return result; } static bool return_zero_if_err(libbpf::bpf_func_id func_id) { switch (func_id) { // When these function fails, bpftrace stores zero as a result. // A user script can check an error by seeing the value. // Therefore error checks of these functions are omitted if // helper_check_level == 1. case libbpf::BPF_FUNC_probe_read: case libbpf::BPF_FUNC_probe_read_str: case libbpf::BPF_FUNC_probe_read_kernel: case libbpf::BPF_FUNC_probe_read_kernel_str: case libbpf::BPF_FUNC_probe_read_user: case libbpf::BPF_FUNC_probe_read_user_str: case libbpf::BPF_FUNC_map_lookup_elem: return true; default: return false; } return false; } void IRBuilderBPF::CreateHelperError(Value *ctx, Value *return_value, libbpf::bpf_func_id func_id, const location &loc) { assert(ctx && ctx->getType() == getPtrTy()); assert(return_value && return_value->getType() == getInt32Ty()); if (bpftrace_.helper_check_level_ == 0 || (bpftrace_.helper_check_level_ == 1 && return_zero_if_err(func_id))) return; int error_id = async_ids_.helper_error(); bpftrace_.resources.helper_error_info[error_id] = { .func_id = func_id, .loc = loc }; auto elements = AsyncEvent::HelperError().asLLVMType(*this); StructType *helper_error_struct = GetStructType("helper_error_t", elements, true); AllocaInst *buf = CreateAllocaBPF(helper_error_struct, "helper_error_t"); 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); CreateOutput(ctx, buf, struct_size, &loc); CreateLifetimeEnd(buf); } // Report error if a return value < 0 (or return value == 0 if compare_zero is // true) void IRBuilderBPF::CreateHelperErrorCond(Value *ctx, Value *return_value, libbpf::bpf_func_id func_id, const location &loc, bool compare_zero) { assert(ctx && ctx->getType() == getPtrTy()); if (bpftrace_.helper_check_level_ == 0 || (bpftrace_.helper_check_level_ == 1 && return_zero_if_err(func_id))) return; llvm::Function *parent = GetInsertBlock()->getParent(); BasicBlock *helper_failure_block = BasicBlock::Create(module_.getContext(), "helper_failure", parent); BasicBlock *helper_merge_block = BasicBlock::Create(module_.getContext(), "helper_merge", parent); // A return type of most helper functions are Int64Ty but they actually // return int and we need to use Int32Ty value to check if a return // value is negative. TODO: use Int32Ty as a return type auto *ret = CreateIntCast(return_value, getInt32Ty(), true); Value *condition; if (compare_zero) condition = CreateICmpNE(ret, Constant::getNullValue(ret->getType())); else condition = CreateICmpSGE(ret, Constant::getNullValue(ret->getType())); CreateCondBr(condition, helper_merge_block, helper_failure_block); SetInsertPoint(helper_failure_block); CreateHelperError(ctx, ret, func_id, loc); CreateBr(helper_merge_block); SetInsertPoint(helper_merge_block); } void IRBuilderBPF::CreatePath(Value *ctx, Value *buf, Value *path, Value *sz, const location &loc) { // int bpf_d_path(struct path *path, char *buf, u32 sz) // Return: 0 or error FunctionType *d_path_func_type = FunctionType::get( getInt64Ty(), { getPtrTy(), buf->getType(), getInt32Ty() }, false); CallInst *call = CreateHelperCall(libbpf::bpf_func_id::BPF_FUNC_d_path, d_path_func_type, { path, buf, sz }, "d_path", &loc); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_d_path, loc); } void IRBuilderBPF::CreateSeqPrintf(Value *ctx, Value *fmt, Value *fmt_size, Value *data, Value *data_len, const location &loc) { // long bpf_seq_printf(struct seq_file *m, const char *fmt, __u32 fmt_size, // const void *data, __u32 data_len) // Return: 0 or error FunctionType *seq_printf_func_type = FunctionType::get( getInt64Ty(), { getInt64Ty(), getPtrTy(), getInt32Ty(), getPtrTy(), getInt32Ty() }, false); PointerType *seq_printf_func_ptr_type = PointerType::get(seq_printf_func_type, 0); Constant *seq_printf_func = ConstantExpr::getCast( Instruction::IntToPtr, getInt64(libbpf::BPF_FUNC_seq_printf), seq_printf_func_ptr_type); LoadInst *meta = CreateLoad(getPtrTy(), CreateSafeGEP(getInt64Ty(), ctx, getInt64(0)), "meta"); meta->setVolatile(true); Value *seq = CreateLoad(getInt64Ty(), CreateGEP(getInt64Ty(), meta, getInt64(0)), "seq"); CallInst *call = createCall(seq_printf_func_type, seq_printf_func, { seq, fmt, fmt_size, data, data_len }, "seq_printf"); CreateHelperErrorCond(ctx, call, libbpf::BPF_FUNC_seq_printf, loc); } StoreInst *IRBuilderBPF::createAlignedStore(Value *val, Value *ptr, unsigned int align) { return CreateAlignedStore(val, ptr, MaybeAlign(align)); } void IRBuilderBPF::CreateProbeRead(Value *ctx, Value *dst, const SizedType &type, Value *src, const location &loc, std::optional addrSpace) { AddrSpace as = addrSpace ? addrSpace.value() : type.GetAS(); if (!type.IsPtrTy()) return CreateProbeRead(ctx, dst, getInt32(type.GetSize()), src, as, loc); // Pointers are internally always represented as 64-bit integers, matching the // BPF register size (BPF is a 64-bit ISA). This helps to avoid BPF codegen // issues such as truncating PTR_TO_STACK registers using shift operations, // which is disallowed (see https://github.com/bpftrace/bpftrace/pull/2361). // However, when reading pointers from kernel or user memory, we need to use // the appropriate size for the target system. const size_t ptr_size = getPointerStorageTy(as)->getIntegerBitWidth() / 8; #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ // TODO: support 32-bit big-endian systems assert(ptr_size == type.GetSize()); #endif if (ptr_size != type.GetSize()) CreateMemsetBPF(dst, getInt8(0), type.GetSize()); CreateProbeRead(ctx, dst, getInt32(ptr_size), src, as, loc); } llvm::Value *IRBuilderBPF::CreateDatastructElemLoad( const SizedType &type, llvm::Value *ptr, bool isVolatile, std::optional addrSpace) { AddrSpace as = addrSpace ? addrSpace.value() : type.GetAS(); llvm::Type *ptr_storage_ty = getPointerStorageTy(as); if (!type.IsPtrTy() || ptr_storage_ty == getInt64Ty()) return CreateLoad(GetType(type), ptr, isVolatile); assert(GetType(type) == getInt64Ty()); // Pointer size for the given address space doesn't match the BPF-side // representation. Use ptr_storage_ty as the load type and cast the result // back to int64. llvm::Value *expr = CreateLoad(ptr_storage_ty, ptr, isVolatile); return CreateIntCast(expr, getInt64Ty(), false); } llvm::Value *IRBuilderBPF::CreatePtrOffset(const SizedType &type, llvm::Value *index, AddrSpace as) { size_t elem_size = type.IsPtrTy() ? getPointerStorageTy(as)->getIntegerBitWidth() / 8 : type.GetSize(); return CreateMul(index, getInt64(elem_size)); } llvm::Value *IRBuilderBPF::CreateSafeGEP(llvm::Type *ty, llvm::Value *ptr, llvm::ArrayRef offsets, const llvm::Twine &name) { if (!ptr->getType()->isPointerTy()) { assert(ptr->getType()->isIntegerTy()); ptr = CreateIntToPtr(ptr, getPtrTy()); } #if LLVM_VERSION_MAJOR >= 18 if (!preserve_static_offset_) { #if LLVM_VERSION_MAJOR >= 20 preserve_static_offset_ = llvm::Intrinsic::getOrInsertDeclaration( &module_, llvm::Intrinsic::preserve_static_offset); #else preserve_static_offset_ = llvm::Intrinsic::getDeclaration( &module_, llvm::Intrinsic::preserve_static_offset); #endif } ptr = CreateCall(preserve_static_offset_, ptr); #endif // Create the GEP itself; on newer versions of LLVM this coerces the pointer // value into a pointer to the given type, and older versions have guaranteed // a suitable cast above (first from integer, then to the right pointer). return CreateGEP(ty, ptr, offsets, name); } llvm::Type *IRBuilderBPF::getPointerStorageTy(AddrSpace as) { switch (as) { case AddrSpace::user: return getUserPointerStorageTy(); default: return getKernelPointerStorageTy(); } } llvm::Type *IRBuilderBPF::getKernelPointerStorageTy() { static int ptr_width = arch::get_kernel_ptr_width(); return getIntNTy(ptr_width); } llvm::Type *IRBuilderBPF::getUserPointerStorageTy() { // TODO: we don't currently have an easy way of determining the pointer size // of the uprobed process, so assume it's the same as the kernel's for now. return getKernelPointerStorageTy(); } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/irbuilderbpf.h000066400000000000000000000407301477746507000176110ustar00rootroot00000000000000#pragma once #include #include #include #include #include "ast/ast.h" #include "ast/async_ids.h" #include "bpftrace.h" #include "types.h" #define CREATE_ATOMIC_RMW(op, ptr, val, align, order) \ CreateAtomicRMW((op), (ptr), (val), MaybeAlign((align)), (order)) namespace bpftrace { namespace ast { using namespace llvm; class IRBuilderBPF : public IRBuilder<> { public: IRBuilderBPF(LLVMContext &context, Module &module, BPFtrace &bpftrace, AsyncIds &async_ids); AllocaInst *CreateAllocaBPF(llvm::Type *ty, const std::string &name = ""); AllocaInst *CreateAllocaBPF(const SizedType &stype, const std::string &name = ""); AllocaInst *CreateAllocaBPFInit(const SizedType &stype, const std::string &name); AllocaInst *CreateAllocaBPF(int bytes, const std::string &name = ""); void CreateMemsetBPF(Value *ptr, Value *val, uint32_t size); void CreateMemcpyBPF(Value *dst, Value *src, uint32_t size); llvm::Type *GetType(const SizedType &stype, bool emit_codegen_types = true); llvm::Type *GetMapValueType(const SizedType &stype); llvm::ConstantInt *GetIntSameSize(uint64_t C, llvm::Value *expr); llvm::ConstantInt *GetIntSameSize(uint64_t C, llvm::Type *ty); Value *GetMapVar(const std::string &map_name); Value *GetNull(); CallInst *CreateMapLookup(Map &map, Value *key, const std::string &name = "lookup_elem"); Value *CreateMapLookupElem(Value *ctx, Map &map, Value *key, const location &loc); Value *CreateMapLookupElem(Value *ctx, const std::string &map_name, Value *key, SizedType &type, const location &loc); Value *CreatePerCpuMapAggElems(Value *ctx, Map &map, Value *key, const SizedType &type, const location &loc); void CreateMapUpdateElem(Value *ctx, const std::string &map_ident, Value *key, Value *val, const location &loc, int64_t flags = 0); void CreateMapDeleteElem(Value *ctx, Map &map, Value *key, const location &loc); Value *CreateForEachMapElem(Value *ctx, Map &map, Value *callback, Value *callback_ctx, const location &loc); void CreateProbeRead(Value *ctx, Value *dst, llvm::Value *size, Value *src, AddrSpace as, const location &loc); // Emits a bpf_probe_read call in which the size is derived from the SizedType // argument. Has special handling for certain types such as pointers where the // size depends on the host system as well as the probe type. // If provided, the optional AddrSpace argument is used instead of the type's // address space (which may not always be set). void CreateProbeRead(Value *ctx, Value *dest, const SizedType &type, Value *src, const location &loc, std::optional addrSpace = std::nullopt); // Emits the load instruction the type of which is derived from the provided // SizedType. Used to access elements from structures that ctx points to, or // those that have already been pulled onto the BPF stack. Correctly handles // pointer size differences (see CreateProbeRead). llvm::Value *CreateDatastructElemLoad( const SizedType &type, llvm::Value *ptr, bool isVolatile = false, std::optional addrSpace = std::nullopt); CallInst *CreateProbeReadStr(Value *ctx, Value *dst, llvm::Value *size, Value *src, AddrSpace as, const location &loc); CallInst *CreateProbeReadStr(Value *ctx, Value *dst, size_t size, Value *src, AddrSpace as, const location &loc); Value *CreateUSDTReadArgument(Value *ctx, AttachPoint *attach_point, int usdt_location_index, int arg_name, Builtin &builtin, pid_t pid, AddrSpace as, const location &loc); Value *CreateStrncmp(Value *str1, Value *str2, uint64_t n, bool inverse); Value *CreateStrcontains(Value *val1, uint64_t str1_size, Value *val2, uint64_t str2_size); Value *CreateIntegerArrayCmp(Value *ctx, Value *val1, Value *val2, const SizedType &val1_type, const SizedType &val2_type, const bool inverse, const location &loc, MDNode *metadata); CallInst *CreateGetNs(TimestampMode ts, const location &loc); CallInst *CreateJiffies64(const location &loc); CallInst *CreateGetCurrentCgroupId(const location &loc); CallInst *CreateGetUidGid(const location &loc); CallInst *CreateGetNumaId(const location &loc); CallInst *CreateGetCpuId(const location &loc); CallInst *CreateGetCurrentTask(const location &loc); CallInst *CreateGetRandom(const location &loc); CallInst *CreateGetStack(Value *ctx, bool ustack, Value *buf, StackType stack_type, const location &loc); CallInst *CreateGetFuncIp(Value *ctx, const location &loc); CallInst *CreatePerCpuPtr(Value *var, Value *cpu, const location &loc); CallInst *CreateThisCpuPtr(Value *var, const location &loc); CallInst *CreateGetJoinMap(BasicBlock *failure_callback, const location &loc); CallInst *CreateGetStackScratchMap(StackType stack_type, BasicBlock *failure_callback, const location &loc); Value *CreateGetStrAllocation(const std::string &name, const location &loc); Value *CreateGetFmtStringArgsAllocation(StructType *struct_type, const std::string &name, const location &loc); Value *CreateTupleAllocation(const SizedType &tuple_type, const std::string &name, const location &loc); Value *CreateWriteMapValueAllocation(const SizedType &value_type, const std::string &name, const location &loc); Value *CreateVariableAllocationInit(const SizedType &value_type, const std::string &name, const location &loc); Value *CreateMapKeyAllocation(const SizedType &value_type, const std::string &name, const location &loc); void CreateCheckSetRecursion(const location &loc, int early_exit_ret); void CreateUnSetRecursion(const location &loc); CallInst *CreateHelperCall(libbpf::bpf_func_id func_id, FunctionType *helper_type, ArrayRef args, const Twine &Name, const location *loc = nullptr); CallInst *createCall(FunctionType *callee_type, Value *callee, ArrayRef args, const Twine &Name); void CreateGetCurrentComm(Value *ctx, AllocaInst *buf, size_t size, const location &loc); void CreateOutput(Value *ctx, Value *data, size_t size, const location *loc = nullptr); void CreateAtomicIncCounter(const std::string &map_name, uint32_t idx); void CreateMapElemInit(Value *ctx, Map &map, Value *key, Value *val, const location &loc); void CreateMapElemAdd(Value *ctx, Map &map, Value *key, Value *val, const location &loc); void CreateDebugOutput(std::string fmt_str, const std::vector &values, const location &loc); void CreateTracePrintk(Value *fmt, Value *fmt_size, const std::vector &values, const location &loc); void CreateSignal(Value *ctx, Value *sig, const location &loc); void CreateOverrideReturn(Value *ctx, Value *rc); void CreateHelperError(Value *ctx, Value *return_value, libbpf::bpf_func_id func_id, const location &loc); void CreateHelperErrorCond(Value *ctx, Value *return_value, libbpf::bpf_func_id func_id, const location &loc, bool compare_zero = false); StructType *GetStackStructType(bool is_ustack); StructType *GetStructType(std::string name, const std::vector &elements, bool packed = false); Value *CreateGetPid(const location &loc); Value *CreateGetTid(const location &loc); Value *CreateGetPid(Value *ctx, const location &loc); Value *CreateGetTid(Value *ctx, const location &loc); AllocaInst *CreateUSym(Value *ctx, Value *val, int probe_id, const location &loc); Value *CreateRegisterRead(Value *ctx, const std::string &builtin); Value *CreateRegisterRead(Value *ctx, int offset, const std::string &name); Value *CreateKFuncArg(Value *ctx, SizedType &type, std::string &name); Value *CreateRawTracepointArg(Value *ctx, const std::string &builtin); Value *CreateUprobeArgsRecord(Value *ctx, const SizedType &args_type); llvm::Type *UprobeArgsType(const SizedType &args_type); CallInst *CreateSkbOutput(Value *skb, Value *len, AllocaInst *data, size_t size); void CreatePath(Value *ctx, Value *buf, Value *path, Value *sz, const location &loc); void CreateSeqPrintf(Value *ctx, Value *fmt, Value *fmt_size, Value *data, Value *data_len, const location &loc); // For a type T, creates an integer expression representing the byte offset // of the element at the given index in T[]. Used for array dereferences and // pointer arithmetic. llvm::Value *CreatePtrOffset(const SizedType &type, llvm::Value *index, AddrSpace as); // Safely handle pointer references by wrapping the address with the // intrinsic `preserve_static_offset` [1], which will ensure that LLVM does // not apply certain basic optimizations (notably, saving any intermediate // offset from this pointer). This is required for the context pointer, // which, if modified. will trigger an error in the verifier. This method // also automatically handles casts from integers and other pointers; the // output value is always a pointer to `ty`. // // [1] https://reviews.llvm.org/D133361 llvm::Value *CreateSafeGEP(llvm::Type *Ty, llvm::Value *Ptr, llvm::ArrayRef IdxList, const llvm::Twine &Name = ""); StoreInst *createAlignedStore(Value *val, Value *ptr, unsigned align); // moves the insertion point to the start of the function you're inside, // invokes functor, then moves the insertion point back to its original // position. this enables you to emit instructions at the start of your // function. you might want to "hoist" an alloca to make it available to // blocks that do not follow from yours, for example to make $a accessible in // both branches here: // BEGIN { if (nsecs > 0) { $a = 1 } else { $a = 2 } print($a); exit() } void hoist(const std::function &functor); // Returns the integer type used to represent pointers in traced code. llvm::Type *getPointerStorageTy(AddrSpace as); private: Module &module_; BPFtrace &bpftrace_; AsyncIds &async_ids_; CallInst *CreateGetPidTgid(const location &loc); void CreateGetNsPidTgid(Value *ctx, Value *dev, Value *ino, AllocaInst *ret, const location &loc); llvm::Type *BpfPidnsInfoType(); Value *CreateUSDTReadArgument(Value *ctx, struct bcc_usdt_argument *argument, Builtin &builtin, AddrSpace as, const location &loc); CallInst *createMapLookup(const std::string &map_name, Value *key, const std::string &name = "lookup_elem"); CallInst *createPerCpuMapLookup( const std::string &map_name, Value *key, Value *cpu, const std::string &name = "lookup_percpu_elem"); CallInst *createPerCpuMapLookup( const std::string &map_name, Value *key, Value *cpu, PointerType *val_ptr_ty, const std::string &name = "lookup_percpu_elem"); CallInst *createGetScratchMap(const std::string &map_name, const std::string &name, const location &loc, BasicBlock *failure_callback, int key = 0); Value *CreateReadMapValueAllocation(const SizedType &value_type, const std::string &name, const location &loc); Value *createAllocation(globalvars::GlobalVar globalvar, llvm::Type *obj_type, const std::string &name, const location &loc, std::optional> gen_async_id_cb = std::nullopt); void CreateAllocationInit(const SizedType &stype, Value *alloc); Value *createScratchBuffer(globalvars::GlobalVar globalvar, const location &loc, size_t key); libbpf::bpf_func_id selectProbeReadHelper(AddrSpace as, bool str); llvm::Type *getKernelPointerStorageTy(); llvm::Type *getUserPointerStorageTy(); void CreateRingbufOutput(Value *data, size_t size, const location *loc = nullptr); void CreatePerfEventOutput(Value *ctx, Value *data, size_t size, const location *loc = nullptr); void createPerCpuSum(AllocaInst *ret, CallInst *call, const SizedType &type); void createPerCpuMinMax(AllocaInst *ret, AllocaInst *is_ret_set, CallInst *call, const SizedType &type); void createPerCpuAvg(AllocaInst *total, AllocaInst *count, CallInst *call, const SizedType &type); std::map structs_; llvm::Function *preserve_static_offset_ = nullptr; }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/pass_manager.cpp000066400000000000000000000023451477746507000201330ustar00rootroot00000000000000#include "ast/pass_manager.h" #include #include "ast/passes/printer.h" #include "bpftrace.h" namespace bpftrace::ast { namespace { void print(PassContext &ctx, const std::string &name, std::ostream &out) { out << "\nAST after: " << name << std::endl; out << "-------------------\n"; ast::Printer printer(ctx.ast_ctx, out); printer.print(); out << std::endl; } } // namespace void PassManager::AddPass(Pass p) { passes_.push_back(std::move(p)); } PassResult PassManager::Run(PassContext &ctx) { if (bt_debug.find(DebugStage::Ast) != bt_debug.end()) print(ctx, "parser", std::cout); for (auto &pass : passes_) { auto result = pass.Run(ctx); if (bt_debug.find(DebugStage::Ast) != bt_debug.end()) print(ctx, pass.name, std::cout); if (!result.Ok()) return result; } return PassResult::Success(); } PassResult PassResult::Error(const std::string &pass) { return PassResult(pass); } PassResult PassResult::Error(const std::string &pass, int code) { return PassResult(pass, code); } PassResult PassResult::Error(const std::string &pass, const std::string &msg) { return PassResult(pass, msg); } PassResult PassResult::Success() { return PassResult(); } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/pass_manager.h000066400000000000000000000045051477746507000176000ustar00rootroot00000000000000#pragma once #include #include #include #include #include namespace bpftrace { class BPFtrace; namespace ast { class ASTContext; class Program; class SemanticAnalyser; class Pass; // Result of a pass run class PassResult { public: static PassResult Error(const std::string &pass); static PassResult Error(const std::string &pass, int code); static PassResult Error(const std::string &pass, const std::string &msg); static PassResult Success(); // Ok returns whether the pass was successful or not bool Ok() const { return success_; }; const std::optional GetErrorMsg() { return errmsg_; }; const std::optional GetErrorCode() { return errcode_; } // Return the pass in which the failure occurred const std::optional GetErrorPass() { return errpass_; } private: PassResult(const std::string &pass) : success_(false), errpass_(pass) { } PassResult(const std::string &pass, int errcode) : success_(false), errpass_(pass), errcode_(errcode) { } PassResult(const std::string &pass, const std::string &msg) : success_(false), errpass_(pass), errmsg_(msg) { } PassResult(const std::string &pass, int errcode, const std::string &msg) : success_(false), errpass_(pass), errcode_(errcode), errmsg_(msg) { } PassResult() : success_(true) { } bool success_ = false; std::optional errpass_; std::optional errcode_; std::optional errmsg_; }; // Context/config for passes // // Note: Most state should end up in the BPFtrace class instead of here struct PassContext { public: PassContext(BPFtrace &b, ASTContext &ast_ctx) : b(b), ast_ctx(ast_ctx) {}; BPFtrace &b; ASTContext &ast_ctx; }; using PassFPtr = std::function; // Base pass class Pass { public: Pass() = delete; Pass(std::string name, PassFPtr fn) : fn_(fn), name(name) {}; virtual ~Pass() = default; PassResult Run(PassContext &ctx) { return fn_(ctx); }; private: PassFPtr fn_; public: std::string name; }; class PassManager { public: PassManager() = default; void AddPass(Pass p); [[nodiscard]] PassResult Run(PassContext &ctx); private: std::vector passes_; }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/passes/000077500000000000000000000000001477746507000162615ustar00rootroot00000000000000bpftrace-0.23.2/src/ast/passes/codegen_llvm.cpp000066400000000000000000005302341477746507000214320ustar00rootroot00000000000000#include "codegen_llvm.h" #include #include #include #include #include #include #include #include #include #include #if LLVM_VERSION_MAJOR <= 16 #include #endif #include #include #include #include #include #include #include #include #include #if LLVM_VERSION_MAJOR <= 16 #include #endif #include #include #include "arch/arch.h" #include "ast/ast.h" #include "ast/async_event_types.h" #include "ast/codegen_helper.h" #include "ast/signal_bt.h" #include "bpfmap.h" #include "collect_nodes.h" #include "globalvars.h" #include "log.h" #include "tracepoint_format_parser.h" #include "types.h" #include "usdt.h" namespace bpftrace::ast { CodegenLLVM::CodegenLLVM(ASTContext &ctx, BPFtrace &bpftrace) : CodegenLLVM(ctx, bpftrace, std::make_unique()) { } CodegenLLVM::CodegenLLVM(ASTContext &ctx, BPFtrace &bpftrace, std::unique_ptr usdt_helper) : Visitor(ctx), bpftrace_(bpftrace), usdt_helper_(std::move(usdt_helper)), context_(std::make_unique()), module_(std::make_unique("bpftrace", *context_)), async_ids_(AsyncIds()), b_(*context_, *module_, bpftrace, async_ids_), debug_(*module_) { LLVMInitializeBPFTargetInfo(); LLVMInitializeBPFTarget(); LLVMInitializeBPFTargetMC(); LLVMInitializeBPFAsmPrinter(); std::string error_str; auto target = llvm::TargetRegistry::lookupTarget(LLVMTargetTriple, error_str); if (!target) throw FatalUserException( "Could not find bpf llvm target, does your llvm support it?"); target_machine_.reset( target->createTargetMachine(LLVMTargetTriple, "generic", "", TargetOptions(), std::optional())); #if LLVM_VERSION_MAJOR >= 18 target_machine_->setOptLevel(llvm::CodeGenOptLevel::Aggressive); #else target_machine_->setOptLevel(llvm::CodeGenOpt::Aggressive); #endif module_->setTargetTriple(LLVMTargetTriple); module_->setDataLayout(target_machine_->createDataLayout()); debug_.createCompileUnit(dwarf::DW_LANG_C, debug_.file, "bpftrace", false, "", 0, StringRef(), DICompileUnit::DebugEmissionKind::LineTablesOnly); module_->addModuleFlag(llvm::Module::Warning, "Debug Info Version", llvm::DEBUG_METADATA_VERSION); // Set license of BPF programs const std::string license = "GPL"; auto license_var = llvm::dyn_cast(module_->getOrInsertGlobal( "LICENSE", ArrayType::get(b_.getInt8Ty(), license.size() + 1))); license_var->setInitializer( ConstantDataArray::getString(module_->getContext(), license.c_str())); license_var->setSection("license"); } ScopedExpr CodegenLLVM::visit(Integer &integer) { return ScopedExpr(b_.getInt64(integer.n)); } ScopedExpr CodegenLLVM::visit(PositionalParameter ¶m) { switch (param.ptype) { case PositionalParameterType::positional: { std::string pstr = bpftrace_.get_param(param.n, param.is_in_str); if (!param.is_in_str) { if (param.type.IsSigned()) { return ScopedExpr(b_.getInt64(std::stoll(pstr, nullptr, 0))); } else { return ScopedExpr(b_.getInt64(std::stoull(pstr, nullptr, 0))); } } else { auto string_param = llvm::dyn_cast( module_->getOrInsertGlobal( pstr, ArrayType::get(b_.getInt8Ty(), pstr.length() + 1))); string_param->setInitializer( ConstantDataArray::getString(module_->getContext(), pstr)); return ScopedExpr(b_.CreatePtrToInt(string_param, b_.getInt64Ty())); } } case PositionalParameterType::count: return ScopedExpr(b_.getInt64(bpftrace_.num_params())); default: LOG(BUG) << "unknown positional parameter type"; __builtin_unreachable(); } } ScopedExpr CodegenLLVM::visit(String &string) { string.str.resize(string.type.GetSize() - 1); auto string_var = llvm::dyn_cast(module_->getOrInsertGlobal( string.str, ArrayType::get(b_.getInt8Ty(), string.type.GetSize()))); string_var->setInitializer( ConstantDataArray::getString(module_->getContext(), string.str)); return ScopedExpr(string_var); } // NB: we do not resolve identifiers that are structs. That is because in // bpftrace you cannot really instantiate a struct. ScopedExpr CodegenLLVM::visit(Identifier &identifier) { if (bpftrace_.enums_.count(identifier.ident) != 0) { return ScopedExpr( b_.getInt64(std::get<0>(bpftrace_.enums_[identifier.ident]))); } else { LOG(BUG) << "unknown identifier \"" << identifier.ident << "\""; __builtin_unreachable(); } } ScopedExpr CodegenLLVM::kstack_ustack(const std::string &ident, StackType stack_type, const location &loc) { if (!murmur_hash_2_func_) murmur_hash_2_func_ = createMurmurHash2Func(); const bool is_ustack = ident == "ustack"; const auto uint64_size = sizeof(uint64_t); StructType *stack_key_struct = b_.GetStackStructType(is_ustack); AllocaInst *stack_key = b_.CreateAllocaBPF(stack_key_struct, "stack_key"); b_.CreateMemsetBPF(stack_key, b_.getInt8(0), datalayout().getTypeStoreSize(stack_key_struct)); llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *stack_scratch_failure = BasicBlock::Create( module_->getContext(), "stack_scratch_failure", parent); BasicBlock *merge_block = BasicBlock::Create(module_->getContext(), "merge_block", parent); Value *stack_trace = b_.CreateGetStackScratchMap(stack_type, stack_scratch_failure, loc); b_.CreateMemsetBPF(stack_trace, b_.getInt8(0), uint64_size * stack_type.limit); BasicBlock *get_stack_success = BasicBlock::Create(module_->getContext(), "get_stack_success", parent); BasicBlock *get_stack_fail = BasicBlock::Create(module_->getContext(), "get_stack_fail", parent); Value *stack_size = b_.CreateGetStack( ctx_, is_ustack, stack_trace, stack_type, loc); Value *condition = b_.CreateICmpSGE(stack_size, b_.getInt64(0)); b_.CreateCondBr(condition, get_stack_success, get_stack_fail); b_.SetInsertPoint(get_stack_fail); b_.CreateDebugOutput("Failed to get stack. Error: %d", std::vector{ stack_size }, loc); b_.CreateBr(merge_block); b_.SetInsertPoint(get_stack_success); Value *num_frames = b_.CreateUDiv(stack_size, b_.getInt64(uint64_size)); b_.CreateStore(num_frames, b_.CreateGEP(stack_key_struct, stack_key, { b_.getInt64(0), b_.getInt32(1) })); // A random seed (or using pid) is probably unnecessary in this situation // and might hurt storage as the same pids may have the same stack and // we don't need to store it twice Value *seed = b_.getInt64(1); // LLVM-12 produces code that fails the BPF verifier because it // can't determine the bounds of nr_stack_frames. The only thing that seems // to work is truncating the type, which is fine because 255 is long enough. Value *trunc_nr_stack_frames = b_.CreateTrunc(num_frames, b_.getInt8Ty()); // Here we use the murmur2 hash function to create the stack ids because // bpf_get_stackid() is kind of broken by design and can suffer from hash // collisions. // More details here: https://github.com/bpftrace/bpftrace/issues/2962 Value *murmur_hash_2 = b_.CreateCall( murmur_hash_2_func_, { stack_trace, trunc_nr_stack_frames, seed }, "murmur_hash_2"); b_.CreateStore(murmur_hash_2, b_.CreateGEP(stack_key_struct, stack_key, { b_.getInt64(0), b_.getInt32(0) })); // Add the stack and id to the stack map b_.CreateMapUpdateElem( ctx_, stack_type.name(), stack_key, stack_trace, loc, BPF_ANY); b_.CreateBr(merge_block); b_.SetInsertPoint(stack_scratch_failure); b_.CreateDebugOutput("Failed to get stack from scratch map.", std::vector{}, loc); b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); // ustack keys are special: see IRBuilderBPF::GetStackStructType() if (is_ustack) { // store pid b_.CreateStore(b_.CreateGetPid(ctx_, loc), b_.CreateGEP(stack_key_struct, stack_key, { b_.getInt64(0), b_.getInt32(2) })); // store probe id b_.CreateStore(b_.GetIntSameSize(get_probe_id(), stack_key_struct->getTypeAtIndex(3)), b_.CreateGEP(stack_key_struct, stack_key, { b_.getInt64(0), b_.getInt32(3) })); } return ScopedExpr(stack_key); } int CodegenLLVM::get_probe_id() { auto begin = bpftrace_.resources.probe_ids.begin(); auto end = bpftrace_.resources.probe_ids.end(); auto found = std::find(begin, end, probefull_); if (found == end) { bpftrace_.resources.probe_ids.push_back(probefull_); } return std::distance(begin, found); } ScopedExpr CodegenLLVM::visit(Builtin &builtin) { if (builtin.ident == "nsecs") { return ScopedExpr(b_.CreateGetNs(TimestampMode::boot, builtin.loc)); } else if (builtin.ident == "elapsed") { AllocaInst *key = b_.CreateAllocaBPF(b_.getInt64Ty(), "elapsed_key"); b_.CreateStore(b_.getInt64(0), key); auto type = CreateUInt64(); auto start = b_.CreateMapLookupElem( ctx_, to_string(MapType::Elapsed), key, type, builtin.loc); Value *ns_value = b_.CreateGetNs(TimestampMode::boot, builtin.loc); Value *ns_delta = b_.CreateSub(ns_value, start); // start won't be on stack, no need to LifeTimeEnd it b_.CreateLifetimeEnd(key); return ScopedExpr(ns_delta); } else if (builtin.ident == "kstack" || builtin.ident == "ustack") { return kstack_ustack(builtin.ident, builtin.type.stack_type, builtin.loc); } else if (builtin.ident == "pid") { return ScopedExpr(b_.CreateGetPid(ctx_, builtin.loc)); } else if (builtin.ident == "tid") { return ScopedExpr(b_.CreateGetTid(ctx_, builtin.loc)); } else if (builtin.ident == "cgroup") { return ScopedExpr(b_.CreateGetCurrentCgroupId(builtin.loc)); } else if (builtin.ident == "uid" || builtin.ident == "gid" || builtin.ident == "username") { Value *uidgid = b_.CreateGetUidGid(builtin.loc); if (builtin.ident == "uid" || builtin.ident == "username") { return ScopedExpr(b_.CreateAnd(uidgid, 0xffffffff)); } else if (builtin.ident == "gid") { return ScopedExpr(b_.CreateLShr(uidgid, 32)); } __builtin_unreachable(); } else if (builtin.ident == "numaid") { return ScopedExpr(b_.CreateGetNumaId(builtin.loc)); } else if (builtin.ident == "cpu") { Value *cpu = b_.CreateGetCpuId(builtin.loc); return ScopedExpr(b_.CreateZExt(cpu, b_.getInt64Ty())); } else if (builtin.ident == "curtask") { return ScopedExpr(b_.CreateGetCurrentTask(builtin.loc)); } else if (builtin.ident == "rand") { Value *random = b_.CreateGetRandom(builtin.loc); return ScopedExpr(b_.CreateZExt(random, b_.getInt64Ty())); } else if (builtin.ident == "comm") { AllocaInst *buf = b_.CreateAllocaBPF(builtin.type, "comm"); // initializing memory needed for older kernels: b_.CreateMemsetBPF(buf, b_.getInt8(0), builtin.type.GetSize()); b_.CreateGetCurrentComm(ctx_, buf, builtin.type.GetSize(), builtin.loc); return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); } else if (builtin.ident == "func") { // fentry/fexit probes do not have access to registers, so require use of // the get_func_ip helper to get the instruction pointer. // // For [ku]retprobes, the IP register will not be pointing to the function // we want to trace. It may point to a kernel trampoline, or it may point to // the caller of the traced function, as it fires after the "ret" // instruction has executed. // // The get_func_ip helper resolves these issues for us. // // But do not use the it for non-ret [ku]probes (which can be used with // offsets), as the helper will fail for probes placed within a function // (not at the entry). Value *value = nullptr; auto probe_type = probetype(current_attach_point_->provider); if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit || probe_type == ProbeType::kretprobe || probe_type == ProbeType::uretprobe) { value = b_.CreateGetFuncIp(ctx_, builtin.loc); } else { value = b_.CreateRegisterRead(ctx_, builtin.ident); } if (builtin.type.IsUsymTy()) { value = b_.CreateUSym(ctx_, value, get_probe_id(), builtin.loc); return ScopedExpr(value, [this, value]() { b_.CreateLifetimeEnd(value); }); } return ScopedExpr(value); } else if (builtin.is_argx() || builtin.ident == "retval") { auto probe_type = probetype(current_attach_point_->provider); if (builtin.type.is_funcarg) { return ScopedExpr(b_.CreateKFuncArg(ctx_, builtin.type, builtin.ident)); } if (builtin.ident.find("arg") != std::string::npos && probe_type == ProbeType::usdt) { return ScopedExpr( b_.CreateUSDTReadArgument(ctx_, current_attach_point_, current_usdt_location_index_, atoi(builtin.ident.substr(3).c_str()), builtin, bpftrace_.pid(), AddrSpace::user, builtin.loc)); } Value *value = nullptr; if (builtin.is_argx() && probe_type == ProbeType::rawtracepoint) value = b_.CreateRawTracepointArg(ctx_, builtin.ident); else value = b_.CreateRegisterRead(ctx_, builtin.ident); if (builtin.type.IsUsymTy()) { value = b_.CreateUSym(ctx_, value, get_probe_id(), builtin.loc); return ScopedExpr(value, [this, value]() { b_.CreateLifetimeEnd(value); }); } return ScopedExpr(value); } else if (!builtin.ident.compare(0, 4, "sarg") && builtin.ident.size() == 5 && builtin.ident.at(4) >= '0' && builtin.ident.at(4) <= '9') { int sp_offset = arch::sp_offset(); if (sp_offset == -1) { LOG(BUG) << "negative offset for stack pointer"; } int arg_num = atoi(builtin.ident.substr(4).c_str()); Value *sp = b_.CreateRegisterRead(ctx_, sp_offset, "reg_sp"); AllocaInst *dst = b_.CreateAllocaBPF(builtin.type, builtin.ident); // Pointer width is used when calculating the SP offset and the number of // bytes to read from stack for each argument. We pass a pointer SizedType // to CreateProbeRead to make sure it uses the correct read size while // keeping builtin.type an int64. size_t arg_width = b_.getPointerStorageTy(builtin.type.GetAS())->getIntegerBitWidth() / 8; SizedType arg_type = CreatePointer(CreateInt8(), builtin.type.GetAS()); assert(builtin.type.GetSize() == arg_type.GetSize()); Value *src = b_.CreateAdd( sp, b_.getInt64((arg_num + arch::arg_stack_offset()) * arg_width)); b_.CreateProbeRead(ctx_, dst, arg_type, src, builtin.loc); Value *expr = b_.CreateLoad(b_.GetType(builtin.type), dst); b_.CreateLifetimeEnd(dst); return ScopedExpr(expr); } else if (builtin.ident == "probe") { auto probe_str = probefull_; probe_str.resize(builtin.type.GetSize() - 1); auto probe_var = llvm::dyn_cast(module_->getOrInsertGlobal( probe_str, ArrayType::get(b_.getInt8Ty(), builtin.type.GetSize()))); probe_var->setInitializer( ConstantDataArray::getString(module_->getContext(), probe_str)); return ScopedExpr(probe_var); } else if (builtin.ident == "args" && probetype(current_attach_point_->provider) == ProbeType::uprobe) { // uprobe args record is built on stack return ScopedExpr(b_.CreateUprobeArgsRecord(ctx_, builtin.type)); } else if (builtin.ident == "args" || builtin.ident == "ctx") { // ctx is undocumented builtin: for debugging. return ScopedExpr(ctx_); } else if (builtin.ident == "cpid") { pid_t cpid = bpftrace_.child_->pid(); if (cpid < 1) { LOG(BUG) << "Invalid cpid: " << cpid; } return ScopedExpr(b_.getInt64(cpid)); } else if (builtin.ident == "jiffies") { return ScopedExpr(b_.CreateJiffies64(builtin.loc)); } else { LOG(BUG) << "unknown builtin \"" << builtin.ident << "\""; __builtin_unreachable(); } } ScopedExpr CodegenLLVM::visit(Call &call) { if (call.func == "count") { Map &map = *call.map; auto scoped_key = getMapKey(map); b_.CreateMapElemAdd( ctx_, map, scoped_key.value(), b_.getInt64(1), call.loc); return ScopedExpr(); } else if (call.func == "sum") { Map &map = *call.map; ScopedExpr scoped_key = getMapKey(map); ScopedExpr scoped_expr = visit(*call.vargs.front()); // promote int to 64-bit Value *cast = b_.CreateIntCast(scoped_expr.value(), b_.getInt64Ty(), call.vargs.front()->type.IsSigned()); b_.CreateMapElemAdd(ctx_, map, scoped_key.value(), cast, call.loc); return ScopedExpr(); } else if (call.func == "max" || call.func == "min") { bool is_max = call.func == "max"; Map &map = *call.map; ScopedExpr scoped_key = getMapKey(map); CallInst *lookup = b_.CreateMapLookup(map, scoped_key.value()); ScopedExpr scoped_expr = visit(*call.vargs.front()); // promote int to 64-bit Value *expr = b_.CreateIntCast(scoped_expr.value(), b_.getInt64Ty(), call.vargs.front()->type.IsSigned()); llvm::Type *mm_struct_ty = b_.GetMapValueType(map.type); llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *lookup_success_block = BasicBlock::Create(module_->getContext(), "lookup_success", parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_->getContext(), "lookup_failure", parent); BasicBlock *lookup_merge_block = BasicBlock::Create(module_->getContext(), "lookup_merge", parent); Value *lookup_condition = b_.CreateICmpNE( b_.CreateIntCast(lookup, b_.getPtrTy(), true), b_.GetNull(), "lookup_cond"); b_.CreateCondBr(lookup_condition, lookup_success_block, lookup_failure_block); b_.SetInsertPoint(lookup_success_block); Value *mm_val = b_.CreateLoad( b_.getInt64Ty(), b_.CreateGEP(mm_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(0) })); Value *is_set_val = b_.CreateLoad( b_.getInt64Ty(), b_.CreateGEP(mm_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(1) })); BasicBlock *is_set_block = BasicBlock::Create(module_->getContext(), "is_set", parent); BasicBlock *min_max_block = BasicBlock::Create(module_->getContext(), "min_max", parent); Value *is_set_condition = b_.CreateICmpEQ(is_set_val, b_.getInt64(1), "is_set_cond"); // If the value has not been set jump past the min_max_condition b_.CreateCondBr(is_set_condition, is_set_block, min_max_block); b_.SetInsertPoint(is_set_block); Value *min_max_condition; if (is_max) { min_max_condition = map.type.IsSigned() ? b_.CreateICmpSGE(expr, mm_val) : b_.CreateICmpUGE(expr, mm_val); } else { min_max_condition = map.type.IsSigned() ? b_.CreateICmpSGE(mm_val, expr) : b_.CreateICmpUGE(mm_val, expr); } b_.CreateCondBr(min_max_condition, min_max_block, lookup_merge_block); b_.SetInsertPoint(min_max_block); b_.CreateStore( expr, b_.CreateGEP(mm_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore( b_.getInt64(1), b_.CreateGEP(mm_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateBr(lookup_merge_block); b_.SetInsertPoint(lookup_failure_block); AllocaInst *mm_struct = b_.CreateAllocaBPF(mm_struct_ty, "mm_struct"); b_.CreateStore(expr, b_.CreateGEP(mm_struct_ty, mm_struct, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore(b_.getInt64(1), b_.CreateGEP(mm_struct_ty, mm_struct, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateMapUpdateElem( ctx_, map.ident, scoped_key.value(), mm_struct, call.loc); b_.CreateLifetimeEnd(mm_struct); b_.CreateBr(lookup_merge_block); b_.SetInsertPoint(lookup_merge_block); return ScopedExpr(); } else if (call.func == "avg" || call.func == "stats") { Map &map = *call.map; ScopedExpr scoped_key = getMapKey(map); CallInst *lookup = b_.CreateMapLookup(map, scoped_key.value()); ScopedExpr scoped_expr = visit(*call.vargs.front()); // promote int to 64-bit Value *expr = b_.CreateIntCast(scoped_expr.value(), b_.getInt64Ty(), call.vargs.front()->type.IsSigned()); llvm::Type *avg_struct_ty = b_.GetMapValueType(map.type); llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *lookup_success_block = BasicBlock::Create(module_->getContext(), "lookup_success", parent); BasicBlock *lookup_failure_block = BasicBlock::Create(module_->getContext(), "lookup_failure", parent); BasicBlock *lookup_merge_block = BasicBlock::Create(module_->getContext(), "lookup_merge", parent); Value *lookup_condition = b_.CreateICmpNE( b_.CreateIntCast(lookup, b_.getPtrTy(), true), b_.GetNull(), "lookup_cond"); b_.CreateCondBr(lookup_condition, lookup_success_block, lookup_failure_block); b_.SetInsertPoint(lookup_success_block); Value *total_val = b_.CreateLoad(b_.getInt64Ty(), b_.CreateGEP(avg_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(0) })); Value *count_val = b_.CreateLoad(b_.getInt64Ty(), b_.CreateGEP(avg_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateStore(b_.CreateAdd(total_val, expr), b_.CreateGEP(avg_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore(b_.CreateAdd(b_.getInt64(1), count_val), b_.CreateGEP(avg_struct_ty, lookup, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateBr(lookup_merge_block); b_.SetInsertPoint(lookup_failure_block); AllocaInst *avg_struct = b_.CreateAllocaBPF(avg_struct_ty, "avg_struct"); b_.CreateStore(expr, b_.CreateGEP(avg_struct_ty, avg_struct, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore(b_.getInt64(1), b_.CreateGEP(avg_struct_ty, avg_struct, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateMapUpdateElem( ctx_, map.ident, scoped_key.value(), avg_struct, call.loc); b_.CreateLifetimeEnd(avg_struct); b_.CreateBr(lookup_merge_block); b_.SetInsertPoint(lookup_merge_block); return ScopedExpr(); } else if (call.func == "hist") { if (!log2_func_) log2_func_ = createLog2Function(); Map &map = *call.map; // There is only one log2_func_ so the second argument must be passed // as an argument even though it is a constant 0..5 // Possible optimization is create one function per different value // of the second argument. ScopedExpr scoped_arg2 = visit(call.vargs.at(1)); Value *k = b_.CreateIntCast(scoped_arg2.value(), b_.getInt64Ty(), false); ScopedExpr scoped_arg = visit(*call.vargs.front()); // promote int to 64-bit Value *expr = b_.CreateIntCast(scoped_arg.value(), b_.getInt64Ty(), call.vargs.front()->type.IsSigned()); Value *log2 = b_.CreateCall(log2_func_, { expr, k }, "log2"); ScopedExpr scoped_key = getHistMapKey(map, log2, call.loc); b_.CreateMapElemAdd( ctx_, map, scoped_key.value(), b_.getInt64(1), call.loc); return ScopedExpr(); } else if (call.func == "lhist") { if (!linear_func_) linear_func_ = createLinearFunction(); Map &map = *call.map; // prepare arguments auto *value_arg = call.vargs.at(0); auto *min_arg = call.vargs.at(1); auto *max_arg = call.vargs.at(2); auto *step_arg = call.vargs.at(3); auto scoped_value_arg = visit(value_arg); auto scoped_min_arg = visit(min_arg); auto scoped_max_arg = visit(max_arg); auto scoped_step_arg = visit(step_arg); // promote int to 64-bit Value *value = b_.CreateIntCast(scoped_value_arg.value(), b_.getInt64Ty(), call.vargs.front()->type.IsSigned()); Value *min = b_.CreateIntCast(scoped_min_arg.value(), b_.getInt64Ty(), false); Value *max = b_.CreateIntCast(scoped_max_arg.value(), b_.getInt64Ty(), false); Value *step = b_.CreateIntCast(scoped_step_arg.value(), b_.getInt64Ty(), false); Value *linear = b_.CreateCall(linear_func_, { value, min, max, step }, "linear"); ScopedExpr scoped_key = getHistMapKey(map, linear, call.loc); b_.CreateMapElemAdd( ctx_, map, scoped_key.value(), b_.getInt64(1), call.loc); return ScopedExpr(); } else if (call.func == "delete") { auto &arg0 = *call.vargs.at(0); auto &map = static_cast(arg0); // Current API: delete accepts two arguments except in the case of scalar // maps (maps with no keys) in which case it you can just pass it the map // and it will act similar to `clear` e.g. `delete(@scalar);` // Legacy API: delete accepts a single argument that is the map with a // key expression e.g. `delete(@mymap[1, 2]);` or no key if the map // is a scalar auto scoped_key = call.vargs.size() > 1 ? getMapKey(map, call.vargs.at(1)) : getMapKey(map); if (!is_bpf_map_clearable(map_types_[map.ident])) { // store zero instead of calling bpf_map_delete_elem() auto val = b_.CreateWriteMapValueAllocation(map.type, map.ident + "_zero", call.loc); b_.CreateStore(Constant::getNullValue(b_.GetType(map.type)), val); b_.CreateMapUpdateElem( ctx_, map.ident, scoped_key.value(), val, call.loc); return ScopedExpr(); } else { b_.CreateMapDeleteElem(ctx_, map, scoped_key.value(), call.loc); return ScopedExpr(); } } else if (call.func == "has_key") { auto &arg = *call.vargs.at(0); auto &map = static_cast(arg); auto scoped_key = getMapKey(map, call.vargs.at(1)); CallInst *lookup = b_.CreateMapLookup(map, scoped_key.value()); Value *expr = b_.CreateICmpNE(b_.CreateIntCast(lookup, b_.getPtrTy(), true), b_.GetNull(), "has_key"); return ScopedExpr(expr); } else if (call.func == "str") { uint64_t max_strlen = bpftrace_.config_.get(ConfigKeyInt::max_strlen); // Largest read we'll allow = our global string buffer size Value *strlen = b_.getInt64(max_strlen); if (call.vargs.size() > 1) { auto scoped_arg = visit(call.vargs.at(1)); Value *proposed_strlen = scoped_arg.value(); // integer comparison: unsigned less-than-or-equal-to CmpInst::Predicate P = CmpInst::ICMP_ULE; // check whether proposed_strlen is less-than-or-equal-to maximum Value *Cmp = b_.CreateICmp(P, proposed_strlen, strlen, "str.min.cmp"); // select proposed_strlen if it's sufficiently low, otherwise choose // maximum strlen = b_.CreateSelect(Cmp, proposed_strlen, strlen, "str.min.select"); } Value *buf = b_.CreateGetStrAllocation("str", call.loc); b_.CreateMemsetBPF(buf, b_.getInt8(0), max_strlen); auto arg0 = call.vargs.front(); auto scoped_expr = visit(call.vargs.front()); b_.CreateProbeReadStr( ctx_, buf, strlen, scoped_expr.value(), arg0->type.GetAS(), call.loc); if (dyn_cast(buf)) return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); return ScopedExpr(buf); } else if (call.func == "buf") { const uint64_t max_strlen = bpftrace_.config_.get(ConfigKeyInt::max_strlen); // Subtract out metadata headroom uint64_t fixed_buffer_length = max_strlen - sizeof(AsyncEvent::Buf); Value *max_length = b_.getInt64(fixed_buffer_length); Value *length; if (call.vargs.size() > 1) { auto &arg = *call.vargs.at(1); auto scoped_expr = visit(&arg); Value *proposed_length = scoped_expr.value(); 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( cmp, proposed_length, max_length, "length.select"); auto literal_length = bpftrace_.get_int_literal(&arg); if (literal_length) fixed_buffer_length = *literal_length; } else { auto &arg = *call.vargs.at(0); fixed_buffer_length = arg.type.GetNumElements() * arg.type.GetElementTy()->GetSize(); length = b_.getInt32(fixed_buffer_length); } Value *buf = b_.CreateGetStrAllocation("buf", call.loc); auto elements = AsyncEvent::Buf().asLLVMType(b_, fixed_buffer_length); std::ostringstream dynamic_sized_struct_name; dynamic_sized_struct_name << "buffer_" << fixed_buffer_length << "_t"; StructType *buf_struct = b_.GetStructType(dynamic_sized_struct_name.str(), elements, true); 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_struct, buf, { b_.getInt32(0), b_.getInt32(1) }); b_.CreateMemsetBPF(buf_data_offset, b_.getInt8(0), fixed_buffer_length); auto scoped_expr = visit(call.vargs.front()); auto arg0 = call.vargs.front(); b_.CreateProbeRead(ctx_, buf_data_offset, length, scoped_expr.value(), find_addrspace_stack(arg0->type), call.loc); if (dyn_cast(buf)) return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); return ScopedExpr(buf); } else if (call.func == "path") { Value *buf = b_.CreateGetStrAllocation("path", call.loc); b_.CreateMemsetBPF(buf, b_.getInt8(0), bpftrace_.config_.get(ConfigKeyInt::max_strlen)); const uint64_t max_size = bpftrace_.config_.get(ConfigKeyInt::max_strlen); Value *sz; if (call.vargs.size() > 1) { auto scoped_arg = visit(call.vargs.at(1)); Value *pr_sz = b_.CreateIntCast(scoped_arg.value(), b_.getInt32Ty(), false); Value *max_sz = b_.getInt32(max_size); Value *cmp = b_.CreateICmp( CmpInst::ICMP_ULE, pr_sz, max_sz, "path.size.cmp"); sz = b_.CreateSelect(cmp, pr_sz, max_sz, "path.size.select"); } else { sz = b_.getInt32(max_size); } auto scoped_arg = visit(*call.vargs.front()); Value *value = scoped_arg.value(); b_.CreatePath(ctx_, buf, b_.CreateCast(value->getType()->isPointerTy() ? Instruction::BitCast : Instruction::IntToPtr, value, b_.getPtrTy()), sz, call.loc); if (dyn_cast(buf)) return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); return ScopedExpr(buf); } else if (call.func == "kaddr") { uint64_t addr; auto name = bpftrace_.get_string_literal(call.vargs.at(0)); addr = bpftrace_.resolve_kname(name); if (!addr) throw FatalUserException("Failed to resolve kernel symbol: " + name); return ScopedExpr(b_.getInt64(addr)); } else if (call.func == "percpu_kaddr") { auto name = bpftrace_.get_string_literal(call.vargs.at(0)); auto var = DeclareKernelVar(name); Value *percpu_ptr; if (call.vargs.size() == 1) { percpu_ptr = b_.CreateThisCpuPtr(var, call.loc); } else { auto scoped_cpu = visit(call.vargs.at(1)); percpu_ptr = b_.CreatePerCpuPtr(var, scoped_cpu.value(), call.loc); } return ScopedExpr(b_.CreatePtrToInt(percpu_ptr, b_.getInt64Ty())); } else if (call.func == "uaddr") { auto name = bpftrace_.get_string_literal(call.vargs.at(0)); struct symbol sym = {}; int err = bpftrace_.resolve_uname(name, &sym, current_attach_point_->target); if (err < 0 || sym.address == 0) throw FatalUserException("Could not resolve symbol: " + current_attach_point_->target + ":" + name); return ScopedExpr(b_.getInt64(sym.address)); } else if (call.func == "cgroupid") { uint64_t cgroupid; auto path = bpftrace_.get_string_literal(call.vargs.at(0)); cgroupid = bpftrace_.resolve_cgroupid(path); return ScopedExpr(b_.getInt64(cgroupid)); } else if (call.func == "join") { auto arg0 = call.vargs.front(); auto scoped_arg = visit(arg0); auto addrspace = arg0->type.GetAS(); llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *failure_callback = BasicBlock::Create(module_->getContext(), "failure_callback", parent); Value *perfdata = b_.CreateGetJoinMap(failure_callback, call.loc); // arg0 b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::join)), perfdata); b_.CreateStore(b_.getInt64(async_ids_.join()), b_.CreateGEP(b_.getInt8Ty(), perfdata, b_.getInt64(8))); SizedType elem_type = CreatePointer(CreateInt8(), addrspace); size_t ptr_width = b_.getPointerStorageTy(addrspace)->getIntegerBitWidth(); assert(b_.GetType(elem_type) == b_.getInt64Ty()); // temporary that stores the value of arg[i] Value *value = scoped_arg.value(); AllocaInst *arr = b_.CreateAllocaBPF(b_.getInt64Ty(), call.func + "_r0"); b_.CreateProbeRead(ctx_, arr, elem_type, value, call.loc); b_.CreateProbeReadStr( ctx_, b_.CreateGEP(b_.getInt8Ty(), perfdata, b_.getInt64(8 + 8)), bpftrace_.join_argsize_, b_.CreateLoad(b_.getInt64Ty(), arr), addrspace, call.loc); for (unsigned int i = 1; i < bpftrace_.join_argnum_; i++) { // advance to the next array element value = b_.CreateAdd(value, b_.getInt64(ptr_width / 8)); b_.CreateProbeRead(ctx_, arr, elem_type, value, call.loc); b_.CreateProbeReadStr( ctx_, b_.CreateGEP(b_.getInt8Ty(), perfdata, b_.getInt64(8 + 8 + i * bpftrace_.join_argsize_)), bpftrace_.join_argsize_, b_.CreateLoad(b_.getInt64Ty(), arr), addrspace, call.loc); } // emit b_.CreateOutput(ctx_, perfdata, 8 + 8 + bpftrace_.join_argnum_ * bpftrace_.join_argsize_, &call.loc); b_.CreateBr(failure_callback); // if we cannot find a valid map value, we will output nothing and continue b_.SetInsertPoint(failure_callback); return ScopedExpr(); } else if (call.func == "ksym") { // We want to just pass through from the child node. return visit(call.vargs.front()); } else if (call.func == "usym") { auto scoped_arg = visit(call.vargs.front()); return ScopedExpr( b_.CreateUSym(ctx_, scoped_arg.value(), get_probe_id(), call.loc), std::move(scoped_arg)); } else if (call.func == "ntop") { // struct { // int af_type; // union { // char[4] inet4; // char[16] inet6; // } // } //} std::vector elements = { b_.getInt64Ty(), ArrayType::get(b_.getInt8Ty(), 16) }; StructType *inet_struct = b_.GetStructType("inet", elements, false); AllocaInst *buf = b_.CreateAllocaBPF(inet_struct, "inet"); Value *af_offset = b_.CreateGEP(inet_struct, buf, { b_.getInt64(0), b_.getInt32(0) }); Value *af_type; auto inet = call.vargs.at(0); if (call.vargs.size() == 1) { if (inet->type.IsIntegerTy() || inet->type.GetSize() == 4) { af_type = b_.getInt64(AF_INET); } else { af_type = b_.getInt64(AF_INET6); } } else { inet = call.vargs.at(1); auto scoped_arg = visit(call.vargs.at(0)); af_type = b_.CreateIntCast(scoped_arg.value(), b_.getInt64Ty(), true); } b_.CreateStore(af_type, af_offset); Value *inet_offset = b_.CreateGEP(inet_struct, buf, { b_.getInt32(0), b_.getInt32(1) }); b_.CreateMemsetBPF(inet_offset, b_.getInt8(0), 16); auto scoped_inet = visit(inet); if (inet->type.IsArrayTy() || inet->type.IsStringTy()) { b_.CreateProbeRead(ctx_, static_cast(inet_offset), inet->type, scoped_inet.value(), call.loc); } else { b_.CreateStore( b_.CreateIntCast(scoped_inet.value(), b_.getInt32Ty(), false), inet_offset); } return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); } else if (call.func == "pton") { auto af_type = AF_INET; int addr_size = 4; std::string addr = bpftrace_.get_string_literal(call.vargs.at(0)); if (addr.find(":") != std::string::npos) { af_type = AF_INET6; addr_size = 16; } llvm::Type *array_t = ArrayType::get(b_.getInt8Ty(), addr_size); AllocaInst *buf; if (af_type == AF_INET6) { buf = b_.CreateAllocaBPF(array_t, "addr6"); } else { buf = b_.CreateAllocaBPF(array_t, "addr4"); } std::vector dst(addr_size); Value *octet; auto ret = inet_pton(af_type, addr.c_str(), dst.data()); if (ret != 1) { throw FatalUserException("inet_pton() call returns " + std::to_string(ret)); } for (int i = 0; i < addr_size; i++) { octet = b_.getInt8(dst[i]); b_.CreateStore( octet, b_.CreateGEP(array_t, buf, { b_.getInt64(0), b_.getInt64(i) })); } return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); } else if (call.func == "reg") { auto reg_name = bpftrace_.get_string_literal(call.vargs.at(0)); int offset = arch::offset(reg_name); if (offset == -1) { throw FatalUserException("negative offset on reg() call"); } return ScopedExpr( b_.CreateRegisterRead(ctx_, offset, call.func + "_" + reg_name)); } else if (call.func == "printf") { // We overload printf call for iterator probe's seq_printf helper. if (!inside_subprog_ && probetype(current_attach_point_->provider) == ProbeType::iter) { auto nargs = call.vargs.size() - 1; int ptr_size = sizeof(unsigned long); int data_size = 0; // create buffer to store the argument expression values SizedType data_type = CreateArray(nargs, CreateUInt64()); AllocaInst *data = b_.CreateAllocaBPFInit(data_type, "data"); std::vector scoped_args; scoped_args.reserve(call.vargs.size()); for (size_t i = 1; i < call.vargs.size(); i++) { // process argument expression Expression &arg = *call.vargs.at(i); auto scoped_arg = visit(&arg); Value *value = scoped_arg.value(); // and store it to data area Value *offset = b_.CreateGEP(b_.GetType(data_type), data, { b_.getInt64(0), b_.getInt32(i - 1) }); b_.CreateStore(value, offset); // keep the expression alive, so it's still there // for following seq_printf call scoped_args.emplace_back(std::move(scoped_arg)); data_size += ptr_size; } // pick the current format string auto print_id = async_ids_.bpf_print(); auto fmt = createFmtString(print_id); auto size = bpftrace_.resources.bpf_print_fmts.at(print_id).size() + 1; // and finally the seq_printf call b_.CreateSeqPrintf(ctx_, b_.CreateIntToPtr(fmt, b_.getPtrTy()), b_.getInt32(size), data, b_.getInt32(data_size), call.loc); return ScopedExpr(); } else { createFormatStringCall(call, async_ids_.printf(), bpftrace_.resources.printf_args, "printf", AsyncAction::printf); return ScopedExpr(); } } else if (call.func == "debugf") { auto print_id = async_ids_.bpf_print(); auto fmt = createFmtString(print_id); auto size = bpftrace_.resources.bpf_print_fmts.at(print_id).size() + 1; std::vector values; std::vector exprs; for (size_t i = 1; i < call.vargs.size(); i++) { Expression &arg = *call.vargs.at(i); auto scoped_expr = visit(arg); values.push_back(scoped_expr.value()); exprs.emplace_back(std::move(scoped_expr)); } b_.CreateTracePrintk(b_.CreateIntToPtr(fmt, b_.getPtrTy()), b_.getInt32(size), values, call.loc); return ScopedExpr(); } else if (call.func == "system") { createFormatStringCall(call, async_ids_.system(), bpftrace_.resources.system_args, "system", AsyncAction::syscall); return ScopedExpr(); } else if (call.func == "cat") { createFormatStringCall(call, async_ids_.cat(), bpftrace_.resources.cat_args, "cat", AsyncAction::cat); return ScopedExpr(); } else if (call.func == "exit") { auto elements = AsyncEvent::Exit().asLLVMType(b_); StructType *exit_struct = b_.GetStructType("exit_t", elements, true); AllocaInst *buf = b_.CreateAllocaBPF(exit_struct, "exit"); size_t struct_size = datalayout().getTypeAllocSize(exit_struct); // Fill in exit struct. b_.CreateStore( b_.getInt64(asyncactionint(AsyncAction::exit)), b_.CreateGEP(exit_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); Value *code = b_.getInt8(0); if (call.vargs.size() == 1) { auto scoped_expr = visit(call.vargs.at(0)); code = scoped_expr.value(); } b_.CreateStore( code, b_.CreateGEP(exit_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateOutput(ctx_, buf, struct_size, &call.loc); b_.CreateLifetimeEnd(buf); createRet(); // create an unreachable basic block for all the "dead instructions" that // may come after exit(). If we don't, LLVM will emit the instructions // leading to a `unreachable insn` warning from the verifier BasicBlock *deadcode = BasicBlock::Create(module_->getContext(), "deadcode", b_.GetInsertBlock()->getParent()); b_.SetInsertPoint(deadcode); return ScopedExpr(); } else if (call.func == "print") { auto &arg = *call.vargs.at(0); if (arg.is_map) { auto &map = static_cast(arg); if (map.key_expr) createPrintNonMapCall(call, async_ids_.non_map_print()); else createPrintMapCall(call); } else { createPrintNonMapCall(call, async_ids_.non_map_print()); } return ScopedExpr(); } 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(async_ids_.cgroup_path(), elements.at(0)), b_.CreateGEP(cgroup_path_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); // Store cgroup id auto arg = call.vargs.at(0); auto scoped_expr = visit(arg); b_.CreateStore(scoped_expr.value(), b_.CreateGEP(cgroup_path_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); } else if (call.func == "clear" || call.func == "zero") { auto elements = AsyncEvent::MapEvent().asLLVMType(b_); StructType *event_struct = b_.GetStructType(call.func + "_t", elements, true); auto &arg = *call.vargs.at(0); auto &map = static_cast(arg); AllocaInst *buf = b_.CreateAllocaBPF(event_struct, call.func + "_" + map.ident); 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)), aa_ptr); else b_.CreateStore(b_.GetIntSameSize(asyncactionint(AsyncAction::zero), elements.at(0)), aa_ptr); int id = bpftrace_.resources.maps_info.at(map.ident).id; if (id == -1) { LOG(BUG) << "map id for map \"" << map.ident << "\" not found"; } 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_.CreateOutput(ctx_, buf, getStructSize(event_struct), &call.loc); return ScopedExpr(buf, [this, buf] { b_.CreateLifetimeEnd(buf); }); } else if (call.func == "len") { if (call.vargs.at(0)->type.IsStack()) { auto *arg = call.vargs.at(0); auto scoped_arg = visit(arg); auto *stack_key_struct = b_.GetStackStructType(arg->type.IsUstackTy()); Value *nr_stack_frames = b_.CreateGEP(stack_key_struct, scoped_arg.value(), { b_.getInt64(0), b_.getInt32(1) }); return ScopedExpr( b_.CreateIntCast(b_.CreateLoad(b_.getInt64Ty(), nr_stack_frames), b_.getInt64Ty(), false)); } else /* call.vargs.at(0)->is_map */ { auto &arg = *call.vargs.at(0); auto &map = static_cast(arg); // Some map types used in bpftrace (BPF_MAP_TYPE_(PERCPU_)ARRAY) do not // implement per-cpu counters and bpf_map_sum_elem_count would always // return 0 for them. In our case, those maps typically have a single // element so we can return 1 straight away. // For the rest, use bpf_map_sum_elem_count if available and map supports // it, otherwise fall back to bpf_for_each_map_elem with a custom callback if (map_has_single_elem(map.type, map.key_type)) { return ScopedExpr(b_.getInt64(1)); } else if (bpftrace_.feature_->has_kernel_func( Kfunc::bpf_map_sum_elem_count) && !is_array_map(map.type, map.key_type)) { return ScopedExpr(CreateKernelFuncCall(Kfunc::bpf_map_sum_elem_count, { b_.GetMapVar(map.ident) }, "len")); } else { if (!map_len_func_) map_len_func_ = createMapLenCallback(); return ScopedExpr(b_.CreateForEachMapElem( ctx_, map, map_len_func_, nullptr, call.loc)); } } } else if (call.func == "time") { auto elements = AsyncEvent::Time().asLLVMType(b_); StructType *time_struct = b_.GetStructType(call.func + "_t", elements, true); AllocaInst *buf = b_.CreateAllocaBPF(time_struct, call.func + "_t"); 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(async_ids_.time(), elements.at(1)), b_.CreateGEP(time_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateOutput(ctx_, buf, getStructSize(time_struct), &call.loc); return ScopedExpr(buf, [this, buf] { b_.CreateLifetimeEnd(buf); }); } else if (call.func == "strftime") { auto elements = AsyncEvent::Strftime().asLLVMType(b_); StructType *strftime_struct = b_.GetStructType(call.func + "_t", elements, true); AllocaInst *buf = b_.CreateAllocaBPF(strftime_struct, call.func + "_args"); b_.CreateStore( b_.GetIntSameSize(async_ids_.strftime(), elements.at(0)), b_.CreateGEP(strftime_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore( b_.GetIntSameSize( static_cast::type>( call.type.ts_mode), elements.at(1)), b_.CreateGEP(strftime_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); auto &arg = *call.vargs.at(1); auto scoped_expr = visit(arg); b_.CreateStore( scoped_expr.value(), b_.CreateGEP(strftime_struct, buf, { b_.getInt64(0), b_.getInt32(2) })); return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); } else if (call.func == "kstack" || call.func == "ustack") { return kstack_ustack(call.func, call.type.stack_type, call.loc); } else if (call.func == "signal") { // long bpf_send_signal(u32 sig) auto &arg = *call.vargs.at(0); if (arg.type.IsStringTy()) { auto signame = bpftrace_.get_string_literal(&arg); int sigid = signal_name_to_num(signame); // Should be caught in semantic analyser if (sigid < 1) { LOG(BUG) << "Invalid signal ID for \"" << signame << "\""; } b_.CreateSignal(ctx_, b_.getInt32(sigid), call.loc); return ScopedExpr(); } auto scoped_arg = visit(arg); Value *sig_number = b_.CreateIntCast(scoped_arg.value(), b_.getInt32Ty(), arg.type.IsSigned()); b_.CreateSignal(ctx_, sig_number, call.loc); return ScopedExpr(); } else if (call.func == "strerror") { return visit(call.vargs.front()); } else if (call.func == "strncmp") { auto &left_arg = *call.vargs.at(0); auto &right_arg = *call.vargs.at(1); auto size_opt = bpftrace_.get_int_literal(call.vargs.at(2)); if (!size_opt.has_value()) LOG(BUG) << "Int literal should have been checked in semantic analysis"; uint64_t size = std::min( { static_cast(*size_opt), static_cast(left_arg.type.GetSize()), static_cast(right_arg.type.GetSize()) }); auto left_string = visit(&left_arg); auto right_string = visit(&right_arg); return ScopedExpr(b_.CreateStrncmp( left_string.value(), right_string.value(), size, false)); } else if (call.func == "strcontains") { auto &left_arg = *call.vargs.at(0); auto &right_arg = *call.vargs.at(1); auto left_string = visit(left_arg); auto right_string = visit(right_arg); return ScopedExpr(b_.CreateStrcontains(left_string.value(), left_arg.type.GetSize(), right_string.value(), right_arg.type.GetSize())); } else if (call.func == "override") { // long bpf_override(struct pt_regs *regs, u64 rc) // returns: 0 auto &arg = *call.vargs.at(0); auto scoped_arg = visit(arg); auto expr = b_.CreateIntCast(scoped_arg.value(), b_.getInt64Ty(), arg.type.IsSigned()); b_.CreateOverrideReturn(ctx_, expr); return ScopedExpr(); } else if (call.func == "kptr" || call.func == "uptr") { return visit(call.vargs.at(0)); } else if (call.func == "macaddr") { // MAC addresses are presented as char[6] AllocaInst *buf = b_.CreateAllocaBPFInit(call.type, "macaddr"); auto macaddr = call.vargs.front(); auto scoped_arg = visit(macaddr); if (inBpfMemory(macaddr->type)) b_.CreateMemcpyBPF(buf, scoped_arg.value(), macaddr->type.GetSize()); else b_.CreateProbeRead(ctx_, static_cast(buf), macaddr->type, scoped_arg.value(), call.loc); return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); } else if (call.func == "unwatch") { auto scoped_addr = visit(call.vargs.at(0)); auto elements = AsyncEvent::WatchpointUnwatch().asLLVMType(b_); StructType *unwatch_struct = b_.GetStructType("unwatch_t", elements, true); 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(unwatch_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore( b_.CreateIntCast(scoped_addr.value(), b_.getInt64Ty(), false /* unsigned */), b_.CreateGEP(unwatch_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateOutput(ctx_, buf, struct_size, &call.loc); return ScopedExpr(buf, [this, buf] { b_.CreateLifetimeEnd(buf); }); } else if (call.func == "bswap") { bpftrace::ast::Expression *arg = call.vargs.at(0); auto scoped_arg = visit(call.vargs.at(0)); assert(arg->type.IsIntegerTy()); if (arg->type.GetSize() > 1) { llvm::Type *arg_type = b_.GetType(arg->type); #if LLVM_VERSION_MAJOR >= 20 llvm::Function *swap_fun = Intrinsic::getOrInsertDeclaration( module_.get(), Intrinsic::bswap, { arg_type }); #else llvm::Function *swap_fun = Intrinsic::getDeclaration(module_.get(), Intrinsic::bswap, { arg_type }); #endif return ScopedExpr(b_.CreateCall(swap_fun, { scoped_arg.value() }), std::move(scoped_arg)); } return scoped_arg; } else if (call.func == "skboutput") { auto elements = AsyncEvent::SkbOutput().asLLVMType(b_); StructType *hdr_t = b_.GetStructType("hdr_t", elements, false); AllocaInst *data = b_.CreateAllocaBPF(hdr_t, "hdr"); // The extra 0 here ensures the type of addr_offset will be int64 Value *aid_addr = b_.CreateGEP(hdr_t, data, { b_.getInt64(0), b_.getInt32(0) }); Value *id_addr = b_.CreateGEP(hdr_t, data, { b_.getInt64(0), b_.getInt32(1) }); Value *time_addr = b_.CreateGEP(hdr_t, data, { b_.getInt64(0), b_.getInt32(2) }); b_.CreateStore(b_.getInt64(asyncactionint(AsyncAction::skboutput)), aid_addr); b_.CreateStore(b_.getInt64(async_ids_.skb_output()), id_addr); b_.CreateStore(b_.CreateGetNs(TimestampMode::boot, call.loc), time_addr); auto scoped_skb = visit(call.vargs.at(1)); auto scoped_arg_len = visit(call.vargs.at(2)); Value *len = b_.CreateIntCast(scoped_arg_len.value(), b_.getInt64Ty(), false); Value *ret = b_.CreateSkbOutput( scoped_skb.value(), len, data, getStructSize(hdr_t)); return ScopedExpr(ret); } else if (call.func == "nsecs") { if (call.type.ts_mode == TimestampMode::sw_tai) { if (!bpftrace_.delta_taitime_.has_value()) LOG(BUG) << "Should have been checked in semantic analysis"; uint64_t delta = bpftrace_.delta_taitime_->tv_sec * 1e9 + bpftrace_.delta_taitime_->tv_nsec; Value *ns = b_.CreateGetNs(TimestampMode::boot, call.loc); return ScopedExpr(b_.CreateAdd(ns, b_.getInt64(delta))); } else { return ScopedExpr(b_.CreateGetNs(call.type.ts_mode, call.loc)); } } else { LOG(BUG) << "missing codegen for function \"" << call.func << "\""; __builtin_unreachable(); } } ScopedExpr CodegenLLVM::visit(Sizeof &szof) { return ScopedExpr(b_.getInt64(szof.argtype.GetSize())); } ScopedExpr CodegenLLVM::visit(Offsetof &offof) { ssize_t offset = 0; const SizedType *record = &offof.record; for (const auto &field : offof.field) { offset += record->GetField(field).offset; record = &record->GetField(field).type; } return ScopedExpr(b_.getInt64(offset)); } ScopedExpr CodegenLLVM::visit(Map &map) { auto scoped_key = getMapKey(map); auto map_info = bpftrace_.resources.maps_info.find(map.ident); if (map_info == bpftrace_.resources.maps_info.end()) { LOG(BUG) << "map name: \"" << map.ident << "\" not found"; } const auto &val_type = map_info->second.value_type; Value *value; if (canAggPerCpuMapElems(val_type, map_info->second.key_type)) { value = b_.CreatePerCpuMapAggElems( ctx_, map, scoped_key.value(), val_type, map.loc); } else { value = b_.CreateMapLookupElem(ctx_, map, scoped_key.value(), map.loc); } return ScopedExpr(value, [this, value] { if (dyn_cast(value)) b_.CreateLifetimeEnd(value); }); } ScopedExpr CodegenLLVM::visit(Variable &var) { // Arrays and structs are not memcopied for local variables if (needMemcpy(var.type) && !(var.type.IsArrayTy() || var.type.IsRecordTy())) { return ScopedExpr(getVariable(var.ident).value); } else { auto &var_llvm = getVariable(var.ident); return ScopedExpr(b_.CreateLoad(var_llvm.type, var_llvm.value)); } } ScopedExpr CodegenLLVM::binop_string(Binop &binop) { if (binop.op != Operator::EQ && binop.op != Operator::NE) { LOG(BUG) << "missing codegen to string operator \"" << opstr(binop) << "\""; } // strcmp returns 0 when strings are equal bool inverse = binop.op == Operator::EQ; auto left_string = visit(binop.left); auto right_string = visit(binop.right); size_t len = std::min(binop.left->type.GetSize(), binop.right->type.GetSize()); return ScopedExpr(b_.CreateStrncmp( left_string.value(), right_string.value(), len, inverse)); } ScopedExpr CodegenLLVM::binop_integer_array(Binop &binop) { assert(binop.op == Operator::EQ || binop.op == Operator::NE); // integer array compare returns 0 when arrays are equal bool inverse = binop.op == Operator::EQ; auto scoped_left = visit(binop.left); auto scoped_right = visit(binop.right); Value *left_array_val = scoped_left.value(); Value *right_array_val = scoped_right.value(); auto &left_array_ty = binop.left->type; auto &right_array_ty = binop.right->type; assert(left_array_ty.GetNumElements() == right_array_ty.GetNumElements()); assert(left_array_ty.GetElementTy()->GetSize() == right_array_ty.GetElementTy()->GetSize()); return ScopedExpr(b_.CreateIntegerArrayCmp(ctx_, left_array_val, right_array_val, left_array_ty, right_array_ty, inverse, binop.loc, createLoopMetadata())); } ScopedExpr CodegenLLVM::binop_buf(Binop &binop) { if (binop.op != Operator::EQ && binop.op != Operator::NE) { LOG(BUG) << "missing codegen to buffer operator \"" << opstr(binop) << "\""; } // strcmp returns 0 when strings are equal bool inverse = binop.op == Operator::EQ; auto scoped_left = visit(binop.left); auto scoped_right = visit(binop.right); Value *left_string = scoped_left.value(); Value *right_string = scoped_right.value(); size_t len = std::min(binop.left->type.GetSize(), binop.right->type.GetSize()); return ScopedExpr(b_.CreateStrncmp(left_string, right_string, len, inverse)); } ScopedExpr CodegenLLVM::binop_int(Binop &binop) { auto scoped_left = visit(binop.left); auto scoped_right = visit(binop.right); Value *lhs = scoped_left.value(); Value *rhs = scoped_right.value(); // If left or right is PositionalParameter, that means the syntax is: // str($1 + num) or str(num + $1) // The positional params returns a pointer to a buffer, and the buffer should // live until str() is accepted. Extend the lifetime of the buffer by moving // these into the deletion scoped, where they will run once the value is // consumed. auto del = [l = std::move(scoped_left), r = std::move(scoped_right)] {}; bool lsign = binop.left->type.IsSigned(); bool rsign = binop.right->type.IsSigned(); bool do_signed = lsign && rsign; // Promote operands if necessary auto size = binop.type.GetSize(); lhs = b_.CreateIntCast(lhs, b_.getIntNTy(size * 8), lsign); rhs = b_.CreateIntCast(rhs, b_.getIntNTy(size * 8), rsign); switch (binop.op) { case Operator::EQ: return ScopedExpr(b_.CreateICmpEQ(lhs, rhs), std::move(del)); case Operator::NE: return ScopedExpr(b_.CreateICmpNE(lhs, rhs), std::move(del)); case Operator::LE: return ScopedExpr(do_signed ? b_.CreateICmpSLE(lhs, rhs) : b_.CreateICmpULE(lhs, rhs), std::move(del)); case Operator::GE: return ScopedExpr(do_signed ? b_.CreateICmpSGE(lhs, rhs) : b_.CreateICmpUGE(lhs, rhs), std::move(del)); case Operator::LT: return ScopedExpr(do_signed ? b_.CreateICmpSLT(lhs, rhs) : b_.CreateICmpULT(lhs, rhs), std::move(del)); case Operator::GT: return ScopedExpr(do_signed ? b_.CreateICmpSGT(lhs, rhs) : b_.CreateICmpUGT(lhs, rhs), std::move(del)); case Operator::LEFT: return ScopedExpr(b_.CreateShl(lhs, rhs), std::move(del)); case Operator::RIGHT: return ScopedExpr(b_.CreateLShr(lhs, rhs), std::move(del)); case Operator::PLUS: return ScopedExpr(b_.CreateAdd(lhs, rhs), std::move(del)); case Operator::MINUS: return ScopedExpr(b_.CreateSub(lhs, rhs), std::move(del)); case Operator::MUL: return ScopedExpr(b_.CreateMul(lhs, rhs), std::move(del)); case Operator::DIV: return ScopedExpr(b_.CreateUDiv(lhs, rhs), std::move(del)); case Operator::MOD: // Always do an unsigned modulo operation here even if `do_signed` // is true. bpf instruction set does not support signed division. // We already warn in the semantic analyser that signed modulo can // lead to undefined behavior (because we will treat it as unsigned). return ScopedExpr(b_.CreateURem(lhs, rhs), std::move(del)); case Operator::BAND: return ScopedExpr(b_.CreateAnd(lhs, rhs), std::move(del)); case Operator::BOR: return ScopedExpr(b_.CreateOr(lhs, rhs), std::move(del)); case Operator::BXOR: return ScopedExpr(b_.CreateXor(lhs, rhs), std::move(del)); default: LOG(BUG) << "\"" << opstr(binop) << "\" was handled earlier"; __builtin_unreachable(); } } ScopedExpr CodegenLLVM::binop_ptr(Binop &binop) { auto compare = false; auto arith = false; // Do what C does switch (binop.op) { case Operator::EQ: case Operator::NE: case Operator::LE: case Operator::GE: case Operator::LT: case Operator::GT: compare = true; break; case Operator::LEFT: case Operator::RIGHT: case Operator::MOD: case Operator::BAND: case Operator::BOR: case Operator::BXOR: case Operator::MUL: case Operator::DIV: LOG(BUG) << "binop_ptr: op not implemented for type\"" << opstr(binop) << "\""; break; case Operator::PLUS: case Operator::MINUS: arith = true; break; default: LOG(BUG) << "binop_ptr invalid op \"" << opstr(binop) << "\""; } auto scoped_left = visit(binop.left); auto scoped_right = visit(binop.right); Value *lhs = scoped_left.value(); Value *rhs = scoped_right.value(); // note: the semantic phase blocks invalid combinations if (compare) { switch (binop.op) { case Operator::EQ: return ScopedExpr(b_.CreateICmpEQ(lhs, rhs)); case Operator::NE: return ScopedExpr(b_.CreateICmpNE(lhs, rhs)); case Operator::LE: { return ScopedExpr(b_.CreateICmpULE(lhs, rhs)); } case Operator::GE: { return ScopedExpr(b_.CreateICmpUGE(lhs, rhs)); } case Operator::LT: { return ScopedExpr(b_.CreateICmpULT(lhs, rhs)); } case Operator::GT: { return ScopedExpr(b_.CreateICmpUGT(lhs, rhs)); } default: LOG(BUG) << "invalid op \"" << opstr(binop) << "\""; __builtin_unreachable(); } } else if (arith) { // Cannot use GEP here as LLVM doesn't know its a pointer bool leftptr = binop.left->type.IsPtrTy(); auto &ptr_ty = leftptr ? binop.left->type : binop.right->type; auto &other_ty = leftptr ? binop.right->type : binop.left->type; Value *ptr_expr = leftptr ? lhs : rhs; Value *other_expr = leftptr ? rhs : lhs; if (other_ty.IsIntTy() && other_ty.GetSize() != 8) other_expr = b_.CreateZExt(other_expr, b_.getInt64Ty()); Value *expr = b_.CreatePtrOffset(*ptr_ty.GetPointeeTy(), other_expr, ptr_ty.GetAS()); if (binop.op == Operator::PLUS) return ScopedExpr(b_.CreateAdd(ptr_expr, expr)); else return ScopedExpr(b_.CreateSub(ptr_expr, expr)); } else { LOG(BUG) << "unknown op \"" << opstr(binop) << "\""; __builtin_unreachable(); } } ScopedExpr CodegenLLVM::visit(Binop &binop) { // Handle && and || separately so short circuiting works if (binop.op == Operator::LAND) { return createLogicalAnd(binop); } else if (binop.op == Operator::LOR) { return createLogicalOr(binop); } SizedType &type = binop.left->type; if (binop.left->type.IsPtrTy() || binop.right->type.IsPtrTy()) { return binop_ptr(binop); } else if (type.IsStringTy()) { return binop_string(binop); } else if (type.IsBufferTy()) { return binop_buf(binop); } else if (type.IsArrayTy() && type.GetElementTy()->IsIntegerTy()) { return binop_integer_array(binop); } else { return binop_int(binop); } } ScopedExpr CodegenLLVM::unop_int(Unop &unop) { SizedType &type = unop.expr->type; switch (unop.op) { case Operator::LNOT: { ScopedExpr scoped_expr = visit(unop.expr); auto ty = scoped_expr.value()->getType(); Value *zero_value = Constant::getNullValue(ty); Value *expr = b_.CreateICmpEQ(scoped_expr.value(), zero_value); // CreateICmpEQ() returns 1-bit integer // Cast it to the same type of the operand // Use unsigned extension, otherwise !0 becomes -1 return ScopedExpr(b_.CreateIntCast(expr, ty, false)); } case Operator::BNOT: { ScopedExpr scoped_expr = visit(unop.expr); return ScopedExpr(b_.CreateNot(scoped_expr.value())); } case Operator::MINUS: { ScopedExpr scoped_expr = visit(unop.expr); return ScopedExpr(b_.CreateNeg(scoped_expr.value())); } case Operator::INCREMENT: case Operator::DECREMENT: { return createIncDec(unop); } case Operator::MUL: { // When dereferencing a 32-bit integer, only read in 32-bits, etc. ScopedExpr scoped_expr = visit(unop.expr); auto dst_type = SizedType(type.GetTy(), type.GetSize()); AllocaInst *dst = b_.CreateAllocaBPF(dst_type, "deref"); b_.CreateProbeRead(ctx_, dst, type, scoped_expr.value(), unop.loc); Value *value = b_.CreateIntCast(b_.CreateLoad(b_.GetType(dst_type), dst), b_.getInt64Ty(), type.IsSigned()); b_.CreateLifetimeEnd(dst); return ScopedExpr(value); } default: LOG(BUG) << "unop_int: invalid op \"" << opstr(unop) << "\""; __builtin_unreachable(); } } ScopedExpr CodegenLLVM::unop_ptr(Unop &unop) { SizedType &type = unop.expr->type; switch (unop.op) { case Operator::MUL: { ScopedExpr scoped_expr = visit(unop.expr); if (unop.type.IsIntegerTy() || unop.type.IsPtrTy()) { auto *et = type.GetPointeeTy(); AllocaInst *dst = b_.CreateAllocaBPF(*et, "deref"); b_.CreateProbeRead( ctx_, dst, *et, scoped_expr.value(), unop.loc, type.GetAS()); Value *value = b_.CreateLoad(b_.GetType(*et), dst); b_.CreateLifetimeEnd(dst); return ScopedExpr(value); } return scoped_expr; // Pass as is. } case Operator::INCREMENT: case Operator::DECREMENT: return createIncDec(unop); default: return visit(unop.expr); } } ScopedExpr CodegenLLVM::visit(Unop &unop) { SizedType &type = unop.expr->type; if (type.IsIntegerTy()) { return unop_int(unop); } else if (type.IsPtrTy() || type.IsCtxAccess()) // allow dereferencing args { return unop_ptr(unop); } else { LOG(BUG) << "invalid type (" << type << ") passed to unary operator \"" << opstr(unop) << "\""; __builtin_unreachable(); } } ScopedExpr CodegenLLVM::visit(Ternary &ternary) { llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *left_block = BasicBlock::Create(module_->getContext(), "left", parent); BasicBlock *right_block = BasicBlock::Create(module_->getContext(), "right", parent); BasicBlock *done = BasicBlock::Create(module_->getContext(), "done", parent); // ordering of all the following statements is important Value *buf = nullptr; if (ternary.type.IsStringTy()) { buf = b_.CreateGetStrAllocation("buf", ternary.loc); uint64_t max_strlen = bpftrace_.config_.get(ConfigKeyInt::max_strlen); b_.CreateMemsetBPF(buf, b_.getInt8(0), max_strlen); } else if (!ternary.type.IsIntTy() && !ternary.type.IsNoneTy()) { buf = b_.CreateAllocaBPF(ternary.type); b_.CreateMemsetBPF(buf, b_.getInt8(0), ternary.type.GetSize()); } auto scoped_expr = visit(ternary.cond); Value *cond = scoped_expr.value(); Value *zero_value = Constant::getNullValue(cond->getType()); b_.CreateCondBr(b_.CreateICmpNE(cond, zero_value, "true_cond"), left_block, right_block); if (ternary.type.IsIntTy()) { // fetch selected integer via CreateStore b_.SetInsertPoint(left_block); auto scoped_left = visit(ternary.left); auto left_expr = b_.CreateIntCast(scoped_left.value(), b_.GetType(ternary.type), ternary.type.IsSigned()); b_.CreateBr(done); b_.SetInsertPoint(right_block); auto scoped_right = visit(ternary.right); auto right_expr = b_.CreateIntCast(scoped_right.value(), b_.GetType(ternary.type), ternary.type.IsSigned()); b_.CreateBr(done); b_.SetInsertPoint(done); auto phi = b_.CreatePHI(b_.GetType(ternary.type), 2, "result"); phi->addIncoming(left_expr, left_block); phi->addIncoming(right_expr, right_block); return ScopedExpr(phi); } else if (ternary.type.IsNoneTy()) { // Type::none b_.SetInsertPoint(left_block); visit(*ternary.left); b_.CreateBr(done); b_.SetInsertPoint(right_block); visit(*ternary.right); b_.CreateBr(done); b_.SetInsertPoint(done); return ScopedExpr(); } else { b_.SetInsertPoint(left_block); auto scoped_left = visit(ternary.left); if (ternary.type.IsTupleTy()) { createTupleCopy( ternary.left->type, ternary.type, buf, scoped_left.value()); } else if (needMemcpy(ternary.type)) { b_.CreateMemcpyBPF(buf, scoped_left.value(), ternary.type.GetSize()); } else { b_.CreateStore(scoped_left.value(), buf); } b_.CreateBr(done); b_.SetInsertPoint(right_block); auto scoped_right = visit(ternary.right); if (ternary.type.IsTupleTy()) { createTupleCopy( ternary.right->type, ternary.type, buf, scoped_right.value()); } else if (needMemcpy(ternary.type)) { b_.CreateMemcpyBPF(buf, scoped_right.value(), ternary.type.GetSize()); } else { b_.CreateStore(scoped_right.value(), buf); } b_.CreateBr(done); b_.SetInsertPoint(done); if (dyn_cast(buf)) return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); return ScopedExpr(buf); } } ScopedExpr CodegenLLVM::visit(FieldAccess &acc) { SizedType &type = acc.expr->type; AddrSpace addrspace = acc.expr->type.GetAS(); assert(type.IsRecordTy() || type.IsTupleTy()); auto scoped_arg = visit(*acc.expr); bool is_ctx = type.IsCtxAccess(); bool is_tparg = type.is_tparg; bool is_internal = type.is_internal; bool is_funcarg = type.is_funcarg; bool is_btftype = type.is_btftype; assert(type.IsRecordTy() || type.IsTupleTy()); if (type.is_funcarg) { auto probe_type = probetype(current_attach_point_->provider); if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit) return ScopedExpr(b_.CreateKFuncArg(ctx_, acc.type, acc.field), std::move(scoped_arg)); else if (probe_type == ProbeType::uprobe) { llvm::Type *args_type = b_.UprobeArgsType(type); return readDatastructElemFromStack(std::move(scoped_arg), b_.getInt32(acc.type.funcarg_idx), args_type, acc.type); } } else if (type.IsTupleTy()) { Value *src = b_.CreateGEP(b_.GetType(type), scoped_arg.value(), { b_.getInt32(0), b_.getInt32(acc.index) }); SizedType &elem_type = type.GetFields()[acc.index].type; if (shouldBeInBpfMemoryAlready(elem_type)) { // Extend lifetime of source buffer return ScopedExpr(src, std::move(scoped_arg)); } else { // Lifetime is not extended, it is freed after the load return ScopedExpr(b_.CreateLoad(b_.GetType(elem_type), src)); } } std::string cast_type = is_tparg ? tracepoint_struct_ : type.GetName(); // This overwrites the stored type! type = CreateRecord(cast_type, bpftrace_.structs.Lookup(cast_type)); if (is_ctx) type.MarkCtxAccess(); type.is_tparg = is_tparg; type.is_internal = is_internal; type.is_funcarg = is_funcarg; type.is_btftype = is_btftype; // Restore the addrspace info // struct MyStruct { const int* a; }; $s = (struct MyStruct *)arg0; $s->a type.SetAS(addrspace); auto &field = type.GetField(acc.field); if (inBpfMemory(type)) { return readDatastructElemFromStack( std::move(scoped_arg), b_.getInt64(field.offset), type, field.type); } else { // Structs may contain two kinds of fields that must be handled separately // (bitfields and _data_loc) if (field.type.IsIntTy() && (field.bitfield.has_value() || field.is_data_loc)) { if (field.bitfield.has_value()) { Value *raw; auto field_type = b_.GetType(field.type); if (type.IsCtxAccess()) { // The offset is specified in absolute terms here; and the load // will implicitly convert to the intended field_type. Value *src = b_.CreateSafeGEP(b_.getPtrTy(), scoped_arg.value(), b_.getInt64(field.offset)); raw = b_.CreateLoad(field_type, src, true); } else { // Since `src` is treated as a offset for a constructed probe read, // we are not constrained in the same way. Value *src = b_.CreateAdd(scoped_arg.value(), b_.getInt64(field.offset)); AllocaInst *dst = b_.CreateAllocaBPF(field.type, type.GetName() + "." + acc.field); // memset so verifier doesn't complain about reading uninitialized // stack b_.CreateMemsetBPF(dst, b_.getInt8(0), field.type.GetSize()); b_.CreateProbeRead(ctx_, dst, b_.getInt32(field.bitfield->read_bytes), src, type.GetAS(), acc.loc); raw = b_.CreateLoad(field_type, dst); b_.CreateLifetimeEnd(dst); } size_t rshiftbits; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ rshiftbits = field.bitfield->access_rshift; #else rshiftbits = (field.type.GetSize() - field.bitfield->read_bytes) * 8; rshiftbits += field.bitfield->access_rshift; #endif Value *shifted = b_.CreateLShr(raw, rshiftbits); Value *masked = b_.CreateAnd(shifted, field.bitfield->mask); return ScopedExpr(masked); } else { // `is_data_loc` should only be set if field access is on `args` which // has to be a ctx access assert(type.IsCtxAccess()); // Parser needs to have rewritten field to be a u64 assert(field.type.IsIntTy()); assert(field.type.GetIntBitWidth() == 64); // Top 2 bytes are length (which we'll ignore). Bottom two bytes are // offset which we add to the start of the tracepoint struct. We need // to wrap the context here in a special way to treat it as the // expected pointer type for all versions. Value *value = b_.CreateLoad(b_.getInt32Ty(), b_.CreateSafeGEP(b_.getInt32Ty(), ctx_, b_.getInt64(field.offset / 4))); value = b_.CreateIntCast(value, b_.getInt64Ty(), false); value = b_.CreateAnd(value, b_.getInt64(0xFFFF)); value = b_.CreateSafeGEP(b_.getInt32Ty(), ctx_, value); return ScopedExpr(value); } } else { return probereadDatastructElem(std::move(scoped_arg), b_.getInt64(field.offset), type, field.type, acc.loc, type.GetName() + "." + acc.field); } } } ScopedExpr CodegenLLVM::visit(ArrayAccess &arr) { SizedType &type = arr.expr->type; auto elem_type = type.IsArrayTy() ? *type.GetElementTy() : *type.GetPointeeTy(); // We can allow the lifetime of the index to expire by the time the array // expression is complete, but we must preserve the lifetime of the // expression since the `readDatstructureElemFromStack` method might end up // returning a pointer to live memory produced by the expression. auto scoped_expr = visit(*arr.expr); auto scoped_index = visit(*arr.indexpr); if (inBpfMemory(type)) return readDatastructElemFromStack( std::move(scoped_expr), scoped_index.value(), type, elem_type); else { Value *array = scoped_expr.value(); if (array->getType()->isPointerTy()) { scoped_expr = ScopedExpr(b_.CreatePtrToInt(array, b_.getInt64Ty()), std::move(scoped_expr)); } Value *index = b_.CreateIntCast(scoped_index.value(), b_.getInt64Ty(), type.IsSigned()); Value *offset = b_.CreatePtrOffset(elem_type, index, type.GetAS()); return probereadDatastructElem(std::move(scoped_expr), offset, type, elem_type, arr.loc, "array_access"); } } ScopedExpr CodegenLLVM::visit(Cast &cast) { auto scoped_expr = visit(cast.expr); if (cast.type.IsIntTy()) { auto int_ty = b_.GetType(cast.type); if (cast.expr->type.IsArrayTy()) { // we need to read the array into the integer Value *array = scoped_expr.value(); if (cast.expr->type.is_internal || cast.expr->type.IsCtxAccess() || cast.expr->type.is_btftype) { // array is on the stack - just cast the pointer if (array->getType()->isIntegerTy()) array = b_.CreateIntToPtr(array, b_.getPtrTy()); } else { // array is in memory - need to proberead auto buf = b_.CreateAllocaBPF(cast.type); b_.CreateProbeRead( ctx_, buf, cast.type, array, cast.loc, cast.expr->type.GetAS()); array = buf; } return ScopedExpr(b_.CreateLoad(int_ty, array, true)); } else { return ScopedExpr( b_.CreateIntCast(scoped_expr.value(), b_.getIntNTy(cast.type.GetIntBitWidth()), cast.type.IsSigned(), "cast")); } } else if (cast.type.IsArrayTy() && cast.expr->type.IsIntTy()) { // We need to store the cast integer on stack and reinterpret the pointer to // it to an array pointer. auto v = b_.CreateAllocaBPF(scoped_expr.value()->getType()); b_.CreateStore(scoped_expr.value(), v); return ScopedExpr(v, [this, v] { b_.CreateLifetimeEnd(v); }); } else { // FIXME(amscanne): The existing behavior is to simply pass the existing // expression back up when it is neither an integer nor an array. return scoped_expr; } } void CodegenLLVM::compareStructure(SizedType &our_type, llvm::Type *llvm_type) { // Validate that what we thought the struct looks like // and LLVM made of it are equal to avoid issues. // // As the size is used throughout the semantic phase for // sizing buffers and maps we have to abort if it doesn't // match. // But offset is only used for printing, so we can recover // from that by storing the correct offset. // size_t our_size = our_type.GetSize(); size_t llvm_size = datalayout().getTypeAllocSize(llvm_type); if (llvm_size != our_size) { LOG(BUG) << "Struct size mismatch: expected: " << our_size << ", real: " << llvm_size; } auto *layout = datalayout().getStructLayout( reinterpret_cast(llvm_type)); for (ssize_t i = 0; i < our_type.GetFieldCount(); i++) { ssize_t llvm_offset = layout->getElementOffset(i); auto &field = our_type.GetField(i); ssize_t our_offset = field.offset; if (llvm_offset != our_offset) { LOG(DEBUG) << "Struct offset mismatch for: " << field.type << "(" << i << ")" << ": (llvm) " << llvm_offset << " != " << our_offset; field.offset = llvm_offset; } } } // createTuple // // Constructs a tuple on the scratch buffer or stack from the provided values. Value *CodegenLLVM::createTuple( const SizedType &tuple_type, const std::vector> &vals, const std::string &name, const location &loc) { auto tuple_ty = b_.GetType(tuple_type); size_t tuple_size = datalayout().getTypeAllocSize(tuple_ty); auto buf = b_.CreateTupleAllocation(tuple_type, name, loc); b_.CreateMemsetBPF(buf, b_.getInt8(0), tuple_size); for (size_t i = 0; i < vals.size(); ++i) { auto [val, vloc] = vals[i]; SizedType &type = tuple_type.GetField(i).type; Value *dst = b_.CreateGEP(tuple_ty, buf, { b_.getInt32(0), b_.getInt32(i) }); if (inBpfMemory(type)) b_.CreateMemcpyBPF(dst, val, type.GetSize()); else if (type.IsArrayTy() || type.IsRecordTy()) b_.CreateProbeRead(ctx_, dst, type, val, *vloc); else b_.CreateStore(val, dst); } return buf; } void CodegenLLVM::createTupleCopy(const SizedType &expr_type, const SizedType &var_type, Value *dst_val, Value *src_val) { assert(expr_type.IsTupleTy() && var_type.IsTupleTy()); auto *array_ty = ArrayType::get(b_.getInt8Ty(), expr_type.GetSize()); auto *tuple_ty = b_.GetType(var_type); for (size_t i = 0; i < expr_type.GetFields().size(); ++i) { SizedType &t_type = expr_type.GetField(i).type; Value *offset_val = b_.CreateGEP( array_ty, src_val, { b_.getInt64(0), b_.getInt64(expr_type.GetField(i).offset) }); Value *dst = b_.CreateGEP(tuple_ty, dst_val, { b_.getInt32(0), b_.getInt32(i) }); if (t_type.IsTupleTy() && !t_type.IsSameSizeRecursive(var_type)) { createTupleCopy(t_type, var_type.GetField(i).type, dst, offset_val); } else if (t_type.IsIntTy() && t_type.GetSize() < 8) { // Integers are always stored as 64-bit in map keys b_.CreateStore(b_.CreateIntCast(b_.CreateLoad(b_.GetType(t_type), offset_val), b_.getInt64Ty(), t_type.IsSigned()), dst); } else { b_.CreateMemcpyBPF(dst, offset_val, t_type.GetSize()); } } } ScopedExpr CodegenLLVM::visit(Tuple &tuple) { llvm::Type *tuple_ty = b_.GetType(tuple.type); compareStructure(tuple.type, tuple_ty); std::vector> vals; std::vector scoped_exprs; vals.reserve(tuple.elems.size()); for (Expression *elem : tuple.elems) { auto scoped_expr = visit(elem); vals.push_back({ scoped_expr.value(), &elem->loc }); scoped_exprs.emplace_back(std::move(scoped_expr)); } auto buf = createTuple(tuple.type, vals, "tuple", tuple.loc); if (dyn_cast(buf)) return ScopedExpr(buf, [this, buf]() { b_.CreateLifetimeEnd(buf); }); return ScopedExpr(buf); } ScopedExpr CodegenLLVM::visit(ExprStatement &expr) { return visit(expr.expr); } ScopedExpr CodegenLLVM::visit(AssignMapStatement &assignment) { Map &map = *assignment.map; auto scoped_expr = visit(*assignment.expr); Value *expr = scoped_expr.value(); if (!expr) // Some functions do the assignments themselves. return ScopedExpr(); auto scoped_key = getMapKey(map); auto &expr_type = assignment.expr->type; const auto self_alloca = needAssignMapStatementAllocation(assignment); Value *value = self_alloca ? b_.CreateWriteMapValueAllocation(map.type, map.ident + "_val", assignment.loc) : expr; if (shouldBeInBpfMemoryAlready(expr_type)) { if (!expr_type.IsSameSizeRecursive(map.type)) { b_.CreateMemsetBPF(value, b_.getInt8(0), map.type.GetSize()); if (expr_type.IsTupleTy()) { createTupleCopy(expr_type, map.type, value, expr); } else if (expr_type.IsStringTy()) { b_.CreateMemcpyBPF(value, expr, expr_type.GetSize()); } else { LOG(BUG) << "Type size mismatch. Map Type Size: " << map.type.GetSize() << " Expression Type Size: " << expr_type.GetSize(); } } } else if (map.type.IsRecordTy() || map.type.IsArrayTy()) { if (!expr_type.is_internal) { // expr currently contains a pointer to the struct or array // We now want to read the entire struct/array in so we can save it b_.CreateProbeRead( ctx_, value, map.type, expr, assignment.loc, expr_type.GetAS()); } } else { if (map.type.IsIntTy()) { // Integers are always stored as 64-bit in map values expr = b_.CreateIntCast(expr, b_.getInt64Ty(), map.type.IsSigned()); } b_.CreateStore(expr, value); } b_.CreateMapUpdateElem( ctx_, map.ident, scoped_key.value(), value, assignment.loc); if (self_alloca && dyn_cast(value)) b_.CreateLifetimeEnd(value); return ScopedExpr(); } void CodegenLLVM::maybeAllocVariable(const std::string &var_ident, const SizedType &var_type, const location &loc) { if (maybeGetVariable(var_ident) != nullptr) { // Already been allocated return; } SizedType alloca_type = var_type; // Arrays and structs need not to be copied when assigned to local variables // since they are treated as read-only - it is sufficient to assign // the pointer and do the memcpy/proberead later when necessary if (var_type.IsArrayTy() || var_type.IsRecordTy()) { auto &pointee_type = var_type.IsArrayTy() ? *var_type.GetElementTy() : var_type; alloca_type = CreatePointer(pointee_type, var_type.GetAS()); } auto val = b_.CreateVariableAllocationInit(alloca_type, var_ident, loc); variables_[scope_stack_.back()][var_ident] = VariableLLVM{ val, b_.GetType(alloca_type) }; } VariableLLVM *CodegenLLVM::maybeGetVariable(const std::string &var_ident) { for (auto scope : scope_stack_) { if (auto search_val = variables_[scope].find(var_ident); search_val != variables_[scope].end()) { return &search_val->second; } } return nullptr; } VariableLLVM &CodegenLLVM::getVariable(const std::string &var_ident) { auto *variable = maybeGetVariable(var_ident); if (!variable) { LOG(BUG) << "Can't find variable: " << var_ident << " in this or outer scope"; } return *variable; } ScopedExpr CodegenLLVM::visit(AssignVarStatement &assignment) { Variable &var = *assignment.var; auto scoped_expr = visit(assignment.expr); // In order to assign a value to a variable, the expression has to actually // produce a value. Unfortunately, there are many expressions which currently // do not produce values (and are either valid only the context of a map // assignment, or are otherwise useful only in statements). Therefore, we try // to provide as much information as possible but generally consider this a // bug until it can be resolved. if (!scoped_expr.value()) { LOG(BUG) << "Expression produced no value for variable: " << var.ident; __builtin_unreachable(); } maybeAllocVariable(var.ident, var.type, var.loc); if (var.type.IsArrayTy() || var.type.IsRecordTy()) { // For arrays and structs, only the pointer is stored. However, this means // that we cannot release the underlying memory for any of these types. We // just disarm the scoped expression, and therefore never free any of these // values; this is a bug that matches existing behavior. scoped_expr.disarm(); b_.CreateStore(b_.CreatePtrToInt(scoped_expr.value(), b_.getInt64Ty()), getVariable(var.ident).value); } else if (needMemcpy(var.type)) { auto *val = getVariable(var.ident).value; auto &expr_type = assignment.expr->type; if (!expr_type.IsSameSizeRecursive(var.type)) { b_.CreateMemsetBPF(val, b_.getInt8(0), var.type.GetSize()); if (var.type.IsTupleTy()) { createTupleCopy(expr_type, var.type, val, scoped_expr.value()); } else { b_.CreateMemcpyBPF(val, scoped_expr.value(), expr_type.GetSize()); } } else { b_.CreateMemcpyBPF(val, scoped_expr.value(), expr_type.GetSize()); } } else { b_.CreateStore(scoped_expr.value(), getVariable(var.ident).value); } return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(VarDeclStatement &decl) { Variable &var = *decl.var; if (var.type.IsNoneTy()) { // unused and has no type return ScopedExpr(); } maybeAllocVariable(var.ident, var.type, var.loc); return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(If &if_node) { llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *if_true = BasicBlock::Create(module_->getContext(), "if_body", parent); BasicBlock *if_end = BasicBlock::Create(module_->getContext(), "if_end", parent); BasicBlock *if_else = nullptr; auto scoped_cond = visit(if_node.cond); auto cond_expr = scoped_cond.value(); Value *zero_value = Constant::getNullValue(cond_expr->getType()); Value *cond = b_.CreateICmpNE(cond_expr, zero_value, "true_cond"); // 3 possible flows: // // if condition is true // parent -> if_body -> if_end // // if condition is false, no else // parent -> if_end // // if condition is false, with else // parent -> if_else -> if_end // if (!if_node.else_block->stmts.empty()) { // LLVM doesn't accept empty basic block, only create when needed if_else = BasicBlock::Create(module_->getContext(), "else_body", parent); b_.CreateCondBr(cond, if_true, if_else); } else { b_.CreateCondBr(cond, if_true, if_end); } b_.SetInsertPoint(if_true); auto scoped_del_if_block = visit(*if_node.if_block); b_.CreateBr(if_end); b_.SetInsertPoint(if_end); if (!if_node.else_block->stmts.empty()) { b_.SetInsertPoint(if_else); auto scoped_del_else_block = visit(*if_node.else_block); b_.CreateBr(if_end); b_.SetInsertPoint(if_end); } return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(Unroll &unroll) { for (int i = 0; i < unroll.var; i++) { // Make sure to save/restore async ID state b/c we could be processing // the same async calls multiple times. auto reset_ids = async_ids_.create_reset_ids(); auto scoped_del = visit(unroll.block); if (i != unroll.var - 1) reset_ids(); } return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(Jump &jump) { switch (jump.ident) { case JumpType::RETURN: // return can be used outside of loops if (jump.return_value) { auto scoped_return = visit(jump.return_value); createRet(scoped_return.value()); } else createRet(); break; case JumpType::BREAK: b_.CreateBr(std::get<1>(loops_.back())); break; case JumpType::CONTINUE: b_.CreateBr(std::get<0>(loops_.back())); break; default: LOG(BUG) << "jump: invalid op \"" << opstr(jump) << "\""; __builtin_unreachable(); } // LLVM doesn't like having instructions after an unconditional branch (segv) // This can be avoided by putting all instructions in a unreachable basicblock // which will be optimize out. // // e.g. in the case of `while (..) { $i++; break; $i++ }` the ir will be: // // while_body: // ... // br label %while_end // // while_end: // ... // // unreach: // $i++ // br label %while_cond // llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *unreach = BasicBlock::Create(module_->getContext(), "unreach", parent); b_.SetInsertPoint(unreach); return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(While &while_block) { if (!loop_metadata_) loop_metadata_ = createLoopMetadata(); llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *while_cond = BasicBlock::Create(module_->getContext(), "while_cond", parent); BasicBlock *while_body = BasicBlock::Create(module_->getContext(), "while_body", parent); BasicBlock *while_end = BasicBlock::Create(module_->getContext(), "while_end", parent); loops_.push_back(std::make_tuple(while_cond, while_end)); b_.CreateBr(while_cond); b_.SetInsertPoint(while_cond); auto scoped_cond = visit(while_block.cond); auto cond_expr = scoped_cond.value(); Value *zero_value = Constant::getNullValue(cond_expr->getType()); auto *cond = b_.CreateICmpNE(cond_expr, zero_value, "true_cond"); Instruction *loop_hdr = b_.CreateCondBr(cond, while_body, while_end); loop_hdr->setMetadata(LLVMContext::MD_loop, loop_metadata_); b_.SetInsertPoint(while_body); auto scoped_block = visit(*while_block.block); b_.CreateBr(while_cond); b_.SetInsertPoint(while_end); loops_.pop_back(); return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(For &f) { auto &map = static_cast(*f.expr); Value *ctx = b_.getInt64(0); llvm::Type *ctx_t = nullptr; const auto &ctx_fields = f.ctx_type.GetFields(); if (!ctx_fields.empty()) { // Pack pointers to variables into context struct for use in the callback std::vector ctx_field_types(ctx_fields.size(), b_.getPtrTy()); ctx_t = StructType::create(ctx_field_types, "ctx_t"); ctx = b_.CreateAllocaBPF(ctx_t, "ctx"); for (size_t i = 0; i < ctx_fields.size(); i++) { const auto &field = ctx_fields[i]; auto *field_expr = getVariable(field.name).value; auto *ctx_field_ptr = b_.CreateSafeGEP( ctx_t, ctx, { b_.getInt64(0), b_.getInt32(i) }, "ctx." + field.name); b_.CreateStore(field_expr, ctx_field_ptr); } } scope_stack_.push_back(&f); b_.CreateForEachMapElem( ctx_, map, createForEachMapCallback(f, ctx_t), ctx, f.loc); scope_stack_.pop_back(); return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(Predicate &pred) { llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *pred_false_block = BasicBlock::Create(module_->getContext(), "pred_false", parent); BasicBlock *pred_true_block = BasicBlock::Create(module_->getContext(), "pred_true", parent); auto scoped_expr = visit(pred.expr); // allow unop casts in predicates: auto cast_value = b_.CreateIntCast(scoped_expr.value(), b_.getInt64Ty(), false); auto cmp_value = b_.CreateICmpEQ(cast_value, b_.getInt64(0), "predcond"); b_.CreateCondBr(cmp_value, pred_false_block, pred_true_block); b_.SetInsertPoint(pred_false_block); createRet(); b_.SetInsertPoint(pred_true_block); return ScopedExpr(cmp_value); } ScopedExpr CodegenLLVM::visit(AttachPoint &) { // Empty return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(Block &block) { scope_stack_.push_back(&block); for (Statement *stmt : block.stmts) visit(*stmt); scope_stack_.pop_back(); return ScopedExpr(); } void CodegenLLVM::generateProbe(Probe &probe, const std::string &full_func_id, const std::string &name, FunctionType *func_type, std::optional usdt_location_index, bool dummy) { // tracepoint wildcard expansion, part 3 of 3. Set tracepoint_struct_ for use // by args builtin. auto probe_type = probetype(current_attach_point_->provider); if (probe_type == ProbeType::tracepoint) tracepoint_struct_ = TracepointFormatParser::get_struct_name(full_func_id); int index = current_attach_point_->index() ?: probe.index(); auto func_name = get_function_name_for_probe(name, index, usdt_location_index); auto *func = llvm::Function::Create( func_type, llvm::Function::ExternalLinkage, func_name, module_.get()); func->setSection(get_section_name(func_name)); debug_.createProbeDebugInfo(*func); BasicBlock *entry = BasicBlock::Create(module_->getContext(), "entry", func); b_.SetInsertPoint(entry); // check: do the following 8 lines need to be in the wildcard loop? ctx_ = func->arg_begin(); if (bpftrace_.need_recursion_check_) { b_.CreateCheckSetRecursion(current_attach_point_->loc, getReturnValueForProbe(probe_type)); } if (probe.pred) visit(*probe.pred); variables_.clear(); auto scoped_block = visit(*probe.block); createRet(); if (dummy) { func->eraseFromParent(); return; } auto pt = probetype(current_attach_point_->provider); if ((pt == ProbeType::watchpoint || pt == ProbeType::asyncwatchpoint) && current_attach_point_->func.size()) generateWatchpointSetupProbe( func_type, name, current_attach_point_->address, index); } void CodegenLLVM::add_probe(AttachPoint &ap, Probe &probe, const std::string &name, FunctionType *func_type) { current_attach_point_ = ≈ probefull_ = ap.name(); if (ap.expansion == ExpansionType::MULTI) { // For non-full expansion (currently only multi), we need to avoid // generating the code as the BPF program would fail to load. if (bpftrace_.probe_matcher_->get_matches_for_ap(ap).empty()) return; } if (probetype(ap.provider) == ProbeType::usdt) { auto usdt = usdt_helper_->find(bpftrace_.pid(), ap.target, ap.ns, ap.func); if (!usdt.has_value()) { throw FatalUserException("Failed to find usdt probe: " + probefull_); } else ap.usdt = *usdt; // A "unique" USDT probe can be present in a binary in multiple // locations. One case where this happens is if a function // containing a USDT probe is inlined into a caller. So we must // generate a new program for each instance. We _must_ regenerate // because argument locations may differ between instance locations // (eg arg0. may not be found in the same offset from the same // register in each location) auto reset_ids = async_ids_.create_reset_ids(); current_usdt_location_index_ = 0; for (int i = 0; i < ap.usdt.num_locations; ++i) { reset_ids(); std::string full_func_id = name + "_loc" + std::to_string(i); generateProbe(probe, full_func_id, probefull_, func_type, i); bpftrace_.add_probe(Visitor::ctx_, ap, probe, i); current_usdt_location_index_++; } } else { generateProbe(probe, name, probefull_, func_type); bpftrace_.add_probe(Visitor::ctx_, ap, probe); } current_attach_point_ = nullptr; } ScopedExpr CodegenLLVM::visit(Subprog &subprog) { scope_stack_.push_back(&subprog); std::vector arg_types; // First argument is for passing ctx pointer for output, rest are proper // arguments to the function arg_types.push_back(b_.getPtrTy()); std::transform(subprog.args.begin(), subprog.args.end(), std::back_inserter(arg_types), [this](SubprogArg *arg) { return b_.GetType(arg->type); }); FunctionType *func_type = FunctionType::get(b_.GetType(subprog.return_type), arg_types, 0); auto *func = llvm::Function::Create(func_type, llvm::Function::InternalLinkage, subprog.name(), module_.get()); BasicBlock *entry = BasicBlock::Create(module_->getContext(), "entry", func); b_.SetInsertPoint(entry); variables_.clear(); ctx_ = func->arg_begin(); inside_subprog_ = true; int arg_index = 0; for (SubprogArg *arg : subprog.args) { auto alloca = b_.CreateAllocaBPF(b_.GetType(arg->type), arg->name()); b_.CreateStore(func->getArg(arg_index + 1), alloca); variables_[scope_stack_.back()][arg->name()] = VariableLLVM{ alloca, alloca->getAllocatedType() }; ++arg_index; } for (Statement *stmt : subprog.stmts) visit(*stmt); if (subprog.return_type.IsVoidTy()) createRet(); FunctionPassManager fpm; FunctionAnalysisManager fam; llvm::PassBuilder pb; pb.registerFunctionAnalyses(fam); fpm.addPass(UnreachableBlockElimPass()); fpm.run(*func, fam); scope_stack_.pop_back(); return ScopedExpr(); } void CodegenLLVM::createRet(Value *value) { if (bpftrace_.need_recursion_check_) { b_.CreateUnSetRecursion(current_attach_point_->loc); } // If value is explicitly provided, use it if (value) { b_.CreateRet(value); return; } else if (inside_subprog_) { b_.CreateRetVoid(); return; } int ret_val = getReturnValueForProbe( probetype(current_attach_point_->provider)); b_.CreateRet(b_.getInt64(ret_val)); } int CodegenLLVM::getReturnValueForProbe(ProbeType probe_type) { // Fall back to default return value switch (probe_type) { case ProbeType::invalid: LOG(BUG) << "Returning from invalid probetype"; return 0; case ProbeType::tracepoint: // Classic (ie. *not* raw) tracepoints have a kernel quirk stopping perf // subsystem from seeing a tracepoint event if BPF program returns 0. // This breaks perf in some situations and generally makes such BPF // programs bad citizens. Return 1 instead. return 1; case ProbeType::special: case ProbeType::kprobe: case ProbeType::kretprobe: case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: case ProbeType::profile: case ProbeType::interval: case ProbeType::software: case ProbeType::hardware: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::fentry: case ProbeType::fexit: case ProbeType::iter: case ProbeType::rawtracepoint: return 0; } LOG(BUG) << "Unknown probetype"; return 0; } ScopedExpr CodegenLLVM::visit(Probe &probe) { FunctionType *func_type = FunctionType::get(b_.getInt64Ty(), { b_.getPtrTy() }, // ctx false); // We begin by saving state that gets changed by the codegen pass, so we // can restore it for the next pass (printf_id_, time_id_). auto reset_ids = async_ids_.create_reset_ids(); bool generated = false; for (auto *attach_point : probe.attach_points) { reset_ids(); current_attach_point_ = attach_point; if (probe.need_expansion || attach_point->expansion == ExpansionType::FULL) { // Do expansion - generate a separate LLVM function for each match auto matches = bpftrace_.probe_matcher_->get_matches_for_ap( *attach_point); probe_count_ += matches.size(); uint64_t max_bpf_progs = bpftrace_.config_.get( ConfigKeyInt::max_bpf_progs); if (probe_count_ > max_bpf_progs) { throw FatalUserException( "Your program is trying to generate more than " + std::to_string(probe_count_) + " BPF programs, which exceeds the current limit of " + std::to_string(max_bpf_progs) + ".\nYou can increase the limit through the BPFTRACE_MAX_BPF_PROGS " "environment variable."); } for (auto &match : matches) { reset_ids(); if (attach_point->index() == 0) attach_point->set_index(getNextIndexForProbe()); auto &match_ap = attach_point->create_expansion_copy(Visitor::ctx_, match); add_probe(match_ap, probe, match, func_type); generated = true; } } else { if (probe.index() == 0) probe.set_index(getNextIndexForProbe()); add_probe(*attach_point, probe, attach_point->name(), func_type); generated = true; } } if (!generated) { generateProbe(probe, "dummy", "dummy", func_type, std::nullopt, true); } current_attach_point_ = nullptr; return ScopedExpr(); } ScopedExpr CodegenLLVM::visit(Program &program) { for (Subprog *subprog : program.functions) visit(subprog); for (Probe *probe : program.probes) visit(probe); return ScopedExpr(); } int CodegenLLVM::getNextIndexForProbe() { return next_probe_index_++; } ScopedExpr CodegenLLVM::getMapKey(Map &map) { return getMapKey(map, map.key_expr); } ScopedExpr CodegenLLVM::getMapKey(Map &map, Expression *key_expr) { const auto alloca_created_here = needMapKeyAllocation(map, key_expr); if (key_expr) { auto scoped_key_expr = visit(key_expr); const auto &key_type = map.key_type; // Allocation needs to be done after recursing via vist(key_expr) so that // we have the expression SSA value. Value *key = alloca_created_here ? b_.CreateMapKeyAllocation(key_type, map.ident + "_key", key_expr->loc) : scoped_key_expr.value(); if (inBpfMemory(key_expr->type)) { if (!key_expr->type.IsSameSizeRecursive(key_type)) { b_.CreateMemsetBPF(key, b_.getInt8(0), key_type.GetSize()); if (key_expr->type.IsTupleTy()) { createTupleCopy( key_expr->type, key_type, key, scoped_key_expr.value()); } else if (key_expr->type.IsStringTy()) { b_.CreateMemcpyBPF(key, scoped_key_expr.value(), key_expr->type.GetSize()); } else { LOG(BUG) << "Type size mismatch. Key Type Size: " << key_type.GetSize() << " Expression Type Size: " << key_expr->type.GetSize(); } } else { // Call-ee freed } } else if (map.key_type.IsIntTy()) { // Integers are always stored as 64-bit in map keys b_.CreateStore(b_.CreateIntCast(scoped_key_expr.value(), b_.getInt64Ty(), key_expr->type.IsSigned()), key); } else { if (key_expr->type.IsArrayTy() || key_expr->type.IsRecordTy()) { // We need to read the entire array/struct and save it b_.CreateProbeRead( ctx_, key, key_expr->type, scoped_key_expr.value(), key_expr->loc); } else { b_.CreateStore(b_.CreateIntCast(scoped_key_expr.value(), b_.getInt64Ty(), key_expr->type.IsSigned()), key); } } // Either way we hold on to the original key, to ensure that its lifetime // lasts as long as it may be accessed. if (alloca_created_here && dyn_cast(key)) { return ScopedExpr(key, [this, key, k = std::move(scoped_key_expr)] { b_.CreateLifetimeEnd(key); }); } return ScopedExpr(key, std::move(scoped_key_expr)); } else { // No map key (e.g., @ = 1;). Use 0 as a key. assert(alloca_created_here); Value *key = b_.CreateMapKeyAllocation(CreateUInt64(), map.ident + "_key", map.loc); b_.CreateStore(b_.getInt64(0), key); if (dyn_cast(key)) { return ScopedExpr(key, [this, key] { b_.CreateLifetimeEnd(key); }); } return ScopedExpr(key); } } ScopedExpr CodegenLLVM::getMultiMapKey(Map &map, const std::vector &extra_keys, const location &loc) { size_t size = map.key_type.GetSize(); for (auto *extra_key : extra_keys) { size += module_->getDataLayout().getTypeAllocSize(extra_key->getType()); } // If key ever changes to not be allocated here, be sure to update getMapKey() // as well to take the new lifetime semantics into account. auto key = b_.CreateMapKeyAllocation(CreateArray(size, CreateInt8()), map.ident + "_key", loc); auto *key_type = ArrayType::get(b_.getInt8Ty(), size); int offset = 0; bool aligned = true; // Construct a map key in the stack auto scoped_expr = visit(*map.key_expr); Value *offset_val = b_.CreateGEP(key_type, key, { b_.getInt64(0), b_.getInt64(offset) }); size_t map_key_size = map.key_type.GetSize(); size_t expr_size = map.key_expr->type.GetSize(); if (inBpfMemory(map.key_expr->type)) { if (!map.key_expr->type.IsSameSizeRecursive(map.key_type)) { b_.CreateMemsetBPF(offset_val, b_.getInt8(0), map_key_size); if (map.key_expr->type.IsTupleTy()) { createTupleCopy( map.key_expr->type, map.key_type, offset_val, scoped_expr.value()); } else if (map.key_expr->type.IsStringTy()) { b_.CreateMemcpyBPF(offset_val, scoped_expr.value(), expr_size); } else { LOG(BUG) << "Type size mismatch. Key Type Size: " << map.key_type.GetSize() << " Expression Type Size: " << expr_size; } } else { b_.CreateMemcpyBPF(offset_val, scoped_expr.value(), expr_size); } if ((map_key_size % 8) != 0) aligned = false; } else { if (map.key_expr->type.IsArrayTy() || map.key_expr->type.IsRecordTy()) { // Read the array/struct into the key b_.CreateProbeRead(ctx_, offset_val, map.key_expr->type, scoped_expr.value(), map.key_expr->loc); if ((map_key_size % 8) != 0) aligned = false; } else { // promote map key to 64-bit: Value *key_elem = b_.CreateIntCast(scoped_expr.value(), b_.getInt64Ty(), map.key_expr->type.IsSigned()); if (aligned) b_.CreateStore(key_elem, offset_val); else b_.createAlignedStore(key_elem, offset_val, 1); } } offset += map_key_size; for (auto *extra_key : extra_keys) { Value *offset_val = b_.CreateGEP(key_type, key, { b_.getInt64(0), b_.getInt64(offset) }); if (aligned) b_.CreateStore(extra_key, offset_val); else b_.createAlignedStore(extra_key, offset_val, 1); offset += module_->getDataLayout().getTypeAllocSize(extra_key->getType()); } return ScopedExpr(key, [this, key] { b_.CreateLifetimeEnd(key); }); } ScopedExpr CodegenLLVM::getHistMapKey(Map &map, Value *log2, const location &loc) { if (map.key_expr) return getMultiMapKey(map, { log2 }, loc); auto key = b_.CreateMapKeyAllocation(CreateUInt64(), map.ident + "_key", loc); b_.CreateStore(log2, key); return ScopedExpr(key, [this, key] { if (dyn_cast(key)) b_.CreateLifetimeEnd(key); }); } ScopedExpr CodegenLLVM::createLogicalAnd(Binop &binop) { assert(binop.left->type.IsIntTy() || binop.left->type.IsPtrTy()); assert(binop.right->type.IsIntTy() || binop.right->type.IsPtrTy()); llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *lhs_true_block = BasicBlock::Create(module_->getContext(), "&&_lhs_true", parent); BasicBlock *true_block = BasicBlock::Create(module_->getContext(), "&&_true", parent); BasicBlock *false_block = BasicBlock::Create(module_->getContext(), "&&_false", parent); BasicBlock *merge_block = BasicBlock::Create(module_->getContext(), "&&_merge", parent); Value *result = b_.CreateAllocaBPF(b_.getInt64Ty(), "&&_result"); ScopedExpr scoped_lhs = visit(*binop.left); Value *lhs = scoped_lhs.value(); Value *lhs_zero_value = Constant::getNullValue(lhs->getType()); b_.CreateCondBr(b_.CreateICmpNE(lhs, lhs_zero_value, "lhs_true_cond"), lhs_true_block, false_block); b_.SetInsertPoint(lhs_true_block); ScopedExpr scoped_rhs = visit(*binop.right); Value *rhs = scoped_rhs.value(); Value *rhs_zero_value = Constant::getNullValue(rhs->getType()); b_.CreateCondBr(b_.CreateICmpNE(rhs, rhs_zero_value, "rhs_true_cond"), true_block, false_block); b_.SetInsertPoint(true_block); b_.CreateStore(b_.getInt64(1), result); b_.CreateBr(merge_block); b_.SetInsertPoint(false_block); b_.CreateStore(b_.getInt64(0), result); b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); return ScopedExpr(b_.CreateLoad(b_.getInt64Ty(), result)); } ScopedExpr CodegenLLVM::createLogicalOr(Binop &binop) { assert(binop.left->type.IsIntTy() || binop.left->type.IsPtrTy()); assert(binop.right->type.IsIntTy() || binop.right->type.IsPtrTy()); llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *lhs_false_block = BasicBlock::Create(module_->getContext(), "||_lhs_false", parent); BasicBlock *false_block = BasicBlock::Create(module_->getContext(), "||_false", parent); BasicBlock *true_block = BasicBlock::Create(module_->getContext(), "||_true", parent); BasicBlock *merge_block = BasicBlock::Create(module_->getContext(), "||_merge", parent); Value *result = b_.CreateAllocaBPF(b_.getInt64Ty(), "||_result"); ScopedExpr scoped_lhs = visit(*binop.left); Value *lhs = scoped_lhs.value(); Value *lhs_zero_value = Constant::getNullValue(lhs->getType()); b_.CreateCondBr(b_.CreateICmpNE(lhs, lhs_zero_value, "lhs_true_cond"), true_block, lhs_false_block); b_.SetInsertPoint(lhs_false_block); ScopedExpr scoped_rhs = visit(*binop.right); Value *rhs = scoped_rhs.value(); Value *rhs_zero_value = Constant::getNullValue(rhs->getType()); b_.CreateCondBr(b_.CreateICmpNE(rhs, rhs_zero_value, "rhs_true_cond"), true_block, false_block); b_.SetInsertPoint(false_block); b_.CreateStore(b_.getInt64(0), result); b_.CreateBr(merge_block); b_.SetInsertPoint(true_block); b_.CreateStore(b_.getInt64(1), result); b_.CreateBr(merge_block); b_.SetInsertPoint(merge_block); return ScopedExpr(b_.CreateLoad(b_.getInt64Ty(), result)); } llvm::Function *CodegenLLVM::createLog2Function() { auto ip = b_.saveIP(); // Arguments: VAL (int64), K (0..5) // Maps each power of 2 into N = 2^K buckets, so we can build fine-grained // histograms with low runtime cost. // // Returns: // 0 for VAL < 0 // 1 + VAL for 0 <= VAL < 2^K // 1 + concat(A,B) for VAL >= 2^K, // where // A is the position of the leftmost "1" in VAL, minus K // B are the K bits following the leftmost "1" in VAL // // As an example, if VAL = 225 (0b11100001) and K = 2: // - the leftmost "1" in VAL is at position 8, so A is 8-2=6 (0b110) // - the following bits are "11" so B is 0b11 // and the returned value is 1 + concat(0b110, 0b11) = 1 + 0b11011 = 28 // // log2(int n, int k) // { // if (n < 0) return 0; // mask = (1ul << k) - 1; // if (n <= mask) return n + 1; // n0 = n; // // find leftmost 1 // l = 0; // for (int i = 5; i >= 0; i--) { // threshold = 1ul << (1<= threshold) << i; // n >>= shift; // l += shift; // } // l -= k; // // mask K bits after leftmost 1 // x = (n0 >> l) & mask; // return ((l + 1) << k) + x + 1; // } FunctionType *log2_func_type = FunctionType::get( b_.getInt64Ty(), { b_.getInt64Ty(), b_.getInt64Ty() }, false); auto *log2_func = llvm::Function::Create( log2_func_type, llvm::Function::InternalLinkage, "log2", module_.get()); log2_func->addFnAttr(Attribute::AlwaysInline); log2_func->setSection("helpers"); BasicBlock *entry = BasicBlock::Create(module_->getContext(), "entry", log2_func); b_.SetInsertPoint(entry); // storage for arguments Value *n_alloc = b_.CreateAllocaBPF(CreateUInt64()); b_.CreateStore(log2_func->arg_begin(), n_alloc); Value *k_alloc = b_.CreateAllocaBPF(CreateUInt64()); b_.CreateStore(log2_func->arg_begin() + 1, k_alloc); // 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); Value *n = b_.CreateLoad(b_.getInt64Ty(), n_alloc); Value *zero = b_.getInt64(0); b_.CreateCondBr(b_.CreateICmpSLT(n, zero), is_less_than_zero, is_not_less_than_zero); b_.SetInsertPoint(is_less_than_zero); createRet(zero); b_.SetInsertPoint(is_not_less_than_zero); // first set of buckets (<= mask) Value *one = b_.getInt64(1); Value *k = b_.CreateLoad(b_.getInt64Ty(), k_alloc); Value *mask = b_.CreateSub(b_.CreateShl(one, k), one); 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_.CreateICmpULE(n, mask), is_zero, is_not_zero); b_.SetInsertPoint(is_zero); createRet(b_.CreateAdd(n, one)); b_.SetInsertPoint(is_not_zero); // index of first bit set in n, 1 means bit 0, guaranteed to be >= k Value *l = zero; for (int i = 5; i >= 0; i--) { Value *threshold = b_.getInt64(1ul << (1ul << i)); Value *is_ge = b_.CreateICmpSGE(n, threshold); // cast is important. is_ge = b_.CreateIntCast(is_ge, b_.getInt64Ty(), false); Value *shift = b_.CreateShl(is_ge, i); n = b_.CreateLShr(n, shift); l = b_.CreateAdd(l, shift); } // see algorithm for next steps: // subtract k, so we can move the next k bits of N to position 0 l = b_.CreateSub(l, k); // now find the k bits in n after the first '1' Value *x = b_.CreateAnd( b_.CreateLShr(b_.CreateLoad(b_.getInt64Ty(), n_alloc), l), mask); Value *ret = b_.CreateAdd(l, one); ret = b_.CreateShl(ret, k); // make room for the extra slots ret = b_.CreateAdd(ret, x); ret = b_.CreateAdd(ret, one); createRet(ret); b_.restoreIP(ip); return module_->getFunction("log2"); } llvm::Function *CodegenLLVM::createLinearFunction() { auto ip = b_.saveIP(); // lhist() returns a bucket index for the given value. The first and last // bucket indexes are special: they are 0 for the less-than-range // bucket, and index max_bucket+2 for the greater-than-range bucket. // Indexes 1 to max_bucket+1 span the buckets in the range. // // int lhist(int value, int min, int max, int step) // { // int result; // // if (value < min) // return 0; // if (value > max) // return 1 + (max - min) / step; // result = 1 + (value - min) / step; // // return result; // } // inlined function initialization FunctionType *linear_func_type = FunctionType::get( b_.getInt64Ty(), { b_.getInt64Ty(), b_.getInt64Ty(), b_.getInt64Ty(), b_.getInt64Ty() }, false); auto *linear_func = llvm::Function::Create(linear_func_type, llvm::Function::InternalLinkage, "linear", module_.get()); linear_func->addFnAttr(Attribute::AlwaysInline); linear_func->setSection("helpers"); BasicBlock *entry = BasicBlock::Create(module_->getContext(), "entry", linear_func); b_.SetInsertPoint(entry); // pull in arguments Value *value_alloc = b_.CreateAllocaBPF(CreateUInt64()); Value *min_alloc = b_.CreateAllocaBPF(CreateUInt64()); Value *max_alloc = b_.CreateAllocaBPF(CreateUInt64()); Value *step_alloc = b_.CreateAllocaBPF(CreateUInt64()); Value *result_alloc = b_.CreateAllocaBPF(CreateUInt64()); b_.CreateStore(linear_func->arg_begin() + 0, value_alloc); b_.CreateStore(linear_func->arg_begin() + 1, min_alloc); b_.CreateStore(linear_func->arg_begin() + 2, max_alloc); b_.CreateStore(linear_func->arg_begin() + 3, step_alloc); Value *cmp = nullptr; // algorithm { 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); BasicBlock *ge_min = BasicBlock::Create(module_->getContext(), "lhist.ge_min", linear_func); b_.CreateCondBr(cmp, lt_min, ge_min); b_.SetInsertPoint(lt_min); createRet(b_.getInt64(0)); b_.SetInsertPoint(ge_min); { 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); BasicBlock *gt_max = BasicBlock::Create(module_->getContext(), "lhist.gt_max", linear_func); b_.CreateCondBr(cmp, gt_max, le_max); b_.SetInsertPoint(gt_max); { 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(b_.getInt64Ty(), result_alloc)); } b_.SetInsertPoint(le_max); { 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(b_.getInt64Ty(), result_alloc)); } b_.restoreIP(ip); return module_->getFunction("linear"); } MDNode *CodegenLLVM::createLoopMetadata() { // Create metadata to disable loop unrolling // // For legacy reasons, the first item of a loop metadata node must be // a self-reference. See https://llvm.org/docs/LangRef.html#llvm-loop LLVMContext &context = *context_; MDNode *unroll_disable = MDNode::get( context, MDString::get(context, "llvm.loop.unroll.disable")); MDNode *loopid = MDNode::getDistinct(context, { unroll_disable, unroll_disable }); loopid->replaceOperandWith(0, loopid); return loopid; } void CodegenLLVM::createFormatStringCall(Call &call, int id, const CallArgs &call_args, const std::string &call_name, AsyncAction async_action) { // perf event output has: uint64_t id, vargs // The id maps to bpftrace_.*_args_, and is a way to define the // types and offsets of each of the arguments, and share that between BPF and // user-space for printing. std::vector elements = { b_.getInt64Ty() }; // ID const auto &args = std::get<1>(call_args.at(id)); for (const Field &arg : args) { llvm::Type *ty = b_.GetType(arg.type); elements.push_back(ty); } StructType *fmt_struct = StructType::create(elements, call_name + "_t", false); int struct_size = datalayout().getTypeAllocSize(fmt_struct); // Check that offsets created during resource analysis match what LLVM // expects. This is just a guard rail against bad padding analysis logic. auto *struct_layout = datalayout().getStructLayout(fmt_struct); for (size_t i = 0; i < args.size(); i++) { size_t offset = static_cast(args[i].offset); // +1 for the id field size_t expected_offset = struct_layout->getElementOffset(i + 1); if (offset != expected_offset) LOG(BUG) << "Calculated offset=" << offset << " does not match LLVM offset=" << expected_offset; } Value *fmt_args = b_.CreateGetFmtStringArgsAllocation(fmt_struct, call_name + "_args", call.loc); // The struct is not packed so we need to memset it b_.CreateMemsetBPF(fmt_args, b_.getInt8(0), struct_size); 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; i < call.vargs.size(); i++) { Expression &arg = *call.vargs.at(i); auto scoped_arg = visit(arg); Value *offset = b_.CreateGEP(fmt_struct, fmt_args, { b_.getInt32(0), b_.getInt32(i) }); if (needMemcpy(arg.type)) b_.CreateMemcpyBPF(offset, scoped_arg.value(), arg.type.GetSize()); else if (arg.type.IsIntegerTy() && arg.type.GetSize() < 8) b_.CreateStore(b_.CreateIntCast(scoped_arg.value(), b_.getInt64Ty(), arg.type.IsSigned()), offset); else b_.CreateStore(scoped_arg.value(), offset); } b_.CreateOutput(ctx_, fmt_args, struct_size, &call.loc); if (dyn_cast(fmt_args)) b_.CreateLifetimeEnd(fmt_args); } void CodegenLLVM::generateWatchpointSetupProbe( FunctionType *func_type, const std::string &expanded_probe_name, int arg_num, int index) { auto func_name = get_function_name_for_watchpoint_setup(expanded_probe_name, index); auto *func = llvm::Function::Create( func_type, llvm::Function::ExternalLinkage, func_name, module_.get()); func->setSection(get_section_name(func_name)); debug_.createProbeDebugInfo(*func); BasicBlock *entry = BasicBlock::Create(module_->getContext(), "entry", func); b_.SetInsertPoint(entry); // Send SIGSTOP to curtask if (!current_attach_point_->async) b_.CreateSignal(ctx_, b_.getInt32(SIGSTOP), current_attach_point_->loc); // Pull out function argument Value *ctx = func->arg_begin(); int offset = arch::arg_offset(arg_num); Value *addr = b_.CreateRegisterRead(ctx, offset, "arg" + std::to_string(arg_num)); // Tell userspace to setup the real watchpoint auto elements = AsyncEvent::Watchpoint().asLLVMType(b_); StructType *watchpoint_struct = b_.GetStructType("watchpoint_t", elements, true); AllocaInst *buf = b_.CreateAllocaBPF(watchpoint_struct, "watchpoint"); size_t struct_size = datalayout().getTypeAllocSize(watchpoint_struct); // Fill in perf event struct b_.CreateStore( b_.getInt64(asyncactionint(AsyncAction::watchpoint_attach)), b_.CreateGEP(watchpoint_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); b_.CreateStore( b_.getInt64(async_ids_.watchpoint()), b_.CreateGEP(watchpoint_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); b_.CreateStore( addr, b_.CreateGEP(watchpoint_struct, buf, { b_.getInt64(0), b_.getInt32(2) })); b_.CreateOutput(ctx, buf, struct_size); b_.CreateLifetimeEnd(buf); createRet(); } void CodegenLLVM::createPrintMapCall(Call &call) { auto elements = AsyncEvent::Print().asLLVMType(b_); StructType *print_struct = b_.GetStructType(call.func + "_t", elements, true); auto &arg = *call.vargs.at(0); auto &map = static_cast(arg); AllocaInst *buf = b_.CreateAllocaBPF(print_struct, call.func + "_" + map.ident); // store asyncactionid: b_.CreateStore( b_.getInt64(asyncactionint(AsyncAction::print)), b_.CreateGEP(print_struct, buf, { b_.getInt64(0), b_.getInt32(0) })); int id = bpftrace_.resources.maps_info.at(map.ident).id; if (id == -1) { LOG(BUG) << "map id for map \"" << map.ident << "\" not found"; } 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 // first loops sets the arguments as passed by user. The second one zeros // the rest size_t arg_idx = 1; for (; arg_idx < call.vargs.size(); arg_idx++) { auto scoped_arg = visit(call.vargs.at(arg_idx)); b_.CreateStore( b_.CreateIntCast(scoped_arg.value(), elements.at(arg_idx), false), 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(print_struct, buf, { b_.getInt64(0), b_.getInt32(arg_idx + 1) })); } b_.CreateOutput(ctx_, buf, getStructSize(print_struct), &call.loc); b_.CreateLifetimeEnd(buf); } void CodegenLLVM::createPrintNonMapCall(Call &call, int id) { auto &arg = *call.vargs.at(0); auto scoped_arg = visit(arg); Value *value = scoped_arg.value(); auto elements = AsyncEvent::PrintNonMap().asLLVMType(b_, arg.type.GetSize()); std::ostringstream struct_name; struct_name << call.func << "_" << arg.type.GetTy() << "_" << arg.type.GetSize() << "_t"; StructType *print_struct = b_.GetStructType(struct_name.str(), elements, true); Value *buf = b_.CreateGetFmtStringArgsAllocation(print_struct, struct_name.str(), call.loc); size_t struct_size = datalayout().getTypeAllocSize(print_struct); // Store asyncactionid: 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(print_struct, buf, { b_.getInt64(0), b_.getInt32(1) })); // Store content Value *content_offset = b_.CreateGEP(print_struct, buf, { b_.getInt32(0), b_.getInt32(2) }); b_.CreateMemsetBPF(content_offset, b_.getInt8(0), arg.type.GetSize()); if (needMemcpy(arg.type)) { if (inBpfMemory(arg.type)) b_.CreateMemcpyBPF(content_offset, value, arg.type.GetSize()); else b_.CreateProbeRead(ctx_, content_offset, arg.type, value, arg.loc); } else { b_.CreateStore(value, content_offset); } b_.CreateOutput(ctx_, buf, struct_size, &call.loc); if (dyn_cast(buf)) b_.CreateLifetimeEnd(buf); } void CodegenLLVM::generate_ir() { assert(state_ == State::INIT); auto analyser = CodegenResourceAnalyser(Visitor::ctx_, bpftrace_.config_); auto codegen_resources = analyser.analyse(); generate_maps(bpftrace_.resources, codegen_resources); generate_global_vars(bpftrace_.resources, bpftrace_.config_); auto scoped_del = visit(Visitor::ctx_.root); debug_.finalize(); state_ = State::IR; } void CodegenLLVM::createMapDefinition(const std::string &name, libbpf::bpf_map_type map_type, uint64_t max_entries, const SizedType &key_type, const SizedType &value_type) { DIType *di_key_type = debug_.GetMapKeyType(key_type, value_type, map_type); map_types_.emplace(name, map_type); auto var_name = bpf_map_name(name); auto debuginfo = debug_.createMapEntry( var_name, map_type, max_entries, di_key_type, value_type); // It's sufficient that the global variable has the correct size (struct with // one pointer per field). The actual inner types are defined in debug info. SmallVector elems = { b_.getPtrTy(), b_.getPtrTy() }; if (!value_type.IsNoneTy()) { elems.push_back(b_.getPtrTy()); elems.push_back(b_.getPtrTy()); } auto type = StructType::create(elems, "struct map_t", false); auto var = llvm::dyn_cast( module_->getOrInsertGlobal(var_name, type)); var->setInitializer(ConstantAggregateZero::get(type)); var->setSection(".maps"); var->setDSOLocal(true); var->addDebugInfo(debuginfo); } libbpf::bpf_map_type CodegenLLVM::get_map_type(const SizedType &val_type, const SizedType &key_type) { if (val_type.IsCountTy() && key_type.IsNoneTy()) { return libbpf::BPF_MAP_TYPE_PERCPU_ARRAY; } else if (val_type.NeedsPercpuMap()) { return libbpf::BPF_MAP_TYPE_PERCPU_HASH; } else { return libbpf::BPF_MAP_TYPE_HASH; } } bool CodegenLLVM::is_array_map(const SizedType &val_type, const SizedType &key_type) { auto map_type = get_map_type(val_type, key_type); return map_type == libbpf::BPF_MAP_TYPE_ARRAY || map_type == libbpf::BPF_MAP_TYPE_PERCPU_ARRAY; } // Check if we can special-case the map to have a single element. This is done // for keyless maps BPF_MAP_TYPE_(PERCPU_)ARRAY type. bool CodegenLLVM::map_has_single_elem(const SizedType &val_type, const SizedType &key_type) { return is_array_map(val_type, key_type) && key_type.IsNoneTy(); } // Emit maps in libbpf format so that Clang can create BTF info for them which // can be read and used by libbpf. // // Each map should be defined by a global variable of a struct type with the // following fields: // - "type" map type (e.g. BPF_MAP_TYPE_HASH) // - "max_entries" maximum number of entries // - "key" key type // - "value" value type // // "type" and "max_entries" are integers but they must be represented as // pointers to an array of ints whose dimension defines the specified value. // // "key" and "value" are pointers to the corresponding types. Note that these // are not used for the BPF_MAP_TYPE_RINGBUF map type. // // The most important part is to generate BTF with the above information. This // is done by emitting DWARF which LLVM will convert into BTF. The LLVM type of // the global variable itself is not important, it can simply be a struct with 4 // pointers. // // Note that LLVM will generate BTF which misses some information. This is // normally set by libbpf's linker but since we load BTF directly, we must do // the fixing ourselves, until we start loading BPF programs via bpf_object. // See BpfBytecode::fixupBTF for details. void CodegenLLVM::generate_maps(const RequiredResources &required_resources, const CodegenResources &codegen_resources) { // User-defined maps for (const auto &[name, info] : required_resources.maps_info) { const auto &val_type = info.value_type; const auto &key_type = info.key_type; auto max_entries = bpftrace_.config_.get(ConfigKeyInt::max_map_keys); auto map_type = get_map_type(val_type, key_type); // hist() and lhist() transparently create additional elements in whatever // map they are assigned to. So even if the map looks like it has no keys, // multiple keys are necessary. if (key_type.IsNoneTy() && !val_type.IsHistTy() && !val_type.IsLhistTy()) { max_entries = 1; } createMapDefinition(name, map_type, max_entries, key_type, val_type); } // bpftrace internal maps uint16_t max_stack_limit = 0; for (const StackType &stack_type : codegen_resources.stackid_maps) { createMapDefinition(stack_type.name(), libbpf::BPF_MAP_TYPE_LRU_HASH, 128 << 10, CreateArray(12, CreateInt8()), CreateArray(stack_type.limit, CreateUInt64())); max_stack_limit = std::max(stack_type.limit, max_stack_limit); } if (max_stack_limit > 0) { createMapDefinition(StackType::scratch_name(), libbpf::BPF_MAP_TYPE_PERCPU_ARRAY, 1, CreateInt32(), CreateArray(max_stack_limit, CreateUInt64())); } if (codegen_resources.needs_join_map) { auto value_size = 8 + 8 + bpftrace_.join_argnum_ * bpftrace_.join_argsize_; SizedType value_type = CreateArray(value_size, CreateInt8()); createMapDefinition(to_string(MapType::Join), libbpf::BPF_MAP_TYPE_PERCPU_ARRAY, 1, CreateInt32(), value_type); } if (codegen_resources.needs_elapsed_map) { createMapDefinition(to_string(MapType::Elapsed), libbpf::BPF_MAP_TYPE_HASH, 1, CreateNone(), CreateUInt64()); } if (bpftrace_.need_recursion_check_) { createMapDefinition(to_string(MapType::RecursionPrevention), libbpf::BPF_MAP_TYPE_PERCPU_ARRAY, 1, CreateInt32(), CreateUInt64()); } if (!bpftrace_.feature_->has_map_ringbuf() || required_resources.needs_perf_event_map) { createMapDefinition(to_string(MapType::PerfEvent), libbpf::BPF_MAP_TYPE_PERF_EVENT_ARRAY, get_online_cpus().size(), CreateInt32(), CreateInt32()); } if (bpftrace_.feature_->has_map_ringbuf()) { auto entries = bpftrace_.config_.get(ConfigKeyInt::perf_rb_pages) * 4096; createMapDefinition(to_string(MapType::Ringbuf), libbpf::BPF_MAP_TYPE_RINGBUF, entries, CreateNone(), CreateNone()); } int loss_cnt_key_size = sizeof(bpftrace_.event_loss_cnt_key_) * 8; int loss_cnt_val_size = sizeof(bpftrace_.event_loss_cnt_val_) * 8; createMapDefinition(to_string(MapType::EventLossCounter), libbpf::BPF_MAP_TYPE_ARRAY, 1, CreateInt(loss_cnt_key_size), CreateInt(loss_cnt_val_size)); } void CodegenLLVM::generate_global_vars( const RequiredResources &resources, const ::bpftrace::Config &bpftrace_config) { for (const auto global_var : resources.needed_global_vars) { auto config = bpftrace::globalvars::get_config(global_var); auto type = bpftrace::globalvars::get_type(global_var, resources, bpftrace_config); auto var = llvm::dyn_cast( module_->getOrInsertGlobal(config.name, b_.GetType(type))); var->setInitializer(ConstantAggregateZero::get(b_.GetType(type))); var->setConstant(config.read_only); var->setSection(config.section); var->setExternallyInitialized(true); var->setDSOLocal(true); var->addDebugInfo(debug_.createGlobalVariable(config.name, type)); } } void CodegenLLVM::emit_elf(const std::string &filename) { assert(state_ == State::OPT); std::error_code err; raw_fd_ostream out(filename, err); if (err) throw std::system_error(err.value(), std::generic_category(), "Failed to open: " + filename); emit(out); out.flush(); return; } void CodegenLLVM::optimize() { assert(state_ == State::IR); PipelineTuningOptions pto; pto.LoopUnrolling = false; pto.LoopInterleaving = false; pto.LoopVectorization = false; pto.SLPVectorization = false; llvm::PassBuilder pb(target_machine_.get(), 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); mpm.run(*module_, mam); state_ = State::OPT; } bool CodegenLLVM::verify() { bool ret = llvm::verifyModule(*module_, &errs()); if (ret) { /* verifyModule doesn't end output with end of line of line, print it now */ std::cerr << std::endl; } return !ret; } // Technically we could use LLVM APIs to do a proper disassemble on // the in-memory ELF file. But that is quite complex, as LLVM only // provides fairly low level APIs to do this. // // Since disassembly is a debugging tool, just shell out to llvm-objdump // to keep things simple. static void disassemble(const SmallVector &elf) { std::cout << "\nDisassembled bytecode\n"; std::cout << "---------------------------\n"; FILE *objdump = ::popen("llvm-objdump -d -", "w"); if (!objdump) { LOG(ERROR) << "Failed to spawn llvm-objdump: " << strerror(errno); return; } if (::fwrite(elf.data(), sizeof(char), elf.size(), objdump) != elf.size()) { LOG(ERROR) << "Failed to write ELF to llvm-objdump"; return; } if (auto rc = ::pclose(objdump)) LOG(WARNING) << "llvm-objdump did not exit cleanly: status " << rc; } void CodegenLLVM::emit(raw_pwrite_stream &stream) { legacy::PassManager PM; #if LLVM_VERSION_MAJOR >= 18 auto type = CodeGenFileType::ObjectFile; #else auto type = llvm::CGFT_ObjectFile; #endif if (target_machine_->addPassesToEmitFile(PM, stream, nullptr, type)) LOG(BUG) << "Cannot emit a file of this type"; PM.run(*module_.get()); } BpfBytecode CodegenLLVM::emit(bool dis) { assert(state_ == State::OPT); SmallVector output; raw_svector_ostream os(output); emit(os); assert(!output.empty()); if (dis) disassemble(output); state_ = State::DONE; return BpfBytecode{ output }; } BpfBytecode CodegenLLVM::compile() { generate_ir(); optimize(); return emit(false); } void CodegenLLVM::DumpIR() { DumpIR(std::cout); } void CodegenLLVM::DumpIR(std::ostream &out) { assert(module_.get() != nullptr); raw_os_ostream os(out); 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); } // Read a single element from a compound data structure (i.e. an array or // a struct) that has been pulled onto BPF stack. // Params: // src_data pointer to the entire data structure // index index of the field to read // data_type type of the structure // elem_type type of the element // scoped_del scope deleter for the data structure ScopedExpr CodegenLLVM::readDatastructElemFromStack(ScopedExpr &&scoped_src, Value *index, llvm::Type *data_type, const SizedType &elem_type) { // src_data should contain a pointer to the data structure, but it may be // internally represented as an integer and then we need to cast it Value *src_data = scoped_src.value(); if (src_data->getType()->isIntegerTy()) src_data = b_.CreateIntToPtr(src_data, b_.getPtrTy()); Value *src = b_.CreateGEP(data_type, src_data, { b_.getInt32(0), index }); if (elem_type.IsIntegerTy() || elem_type.IsPtrTy()) { // Load the correct type from src return ScopedExpr( b_.CreateDatastructElemLoad(elem_type, src, true, elem_type.GetAS())); } else { // The inner type is an aggregate - instead of copying it, just pass // the pointer and extend lifetime of the source data. return ScopedExpr(src, std::move(scoped_src)); } } ScopedExpr CodegenLLVM::readDatastructElemFromStack(ScopedExpr &&scoped_src, Value *index, const SizedType &data_type, const SizedType &elem_type) { return readDatastructElemFromStack( std::move(scoped_src), index, b_.GetType(data_type), elem_type); } // Read a single element from a compound data structure (i.e. an array or // a struct) that has not been yet pulled into BPF memory. // Params: // scoped_src scoped expression pointing to the data structure // offset offset of the requested element from the structure beginning // data_type type of the data structure // elem_type type of the requested element // loc location of the element access (for proberead) // temp_name name of a temporary variable, if the function creates any ScopedExpr CodegenLLVM::probereadDatastructElem(ScopedExpr &&scoped_src, Value *offset, const SizedType &data_type, const SizedType &elem_type, location loc, const std::string &temp_name) { // We treat this access as a raw byte offset, but may then subsequently need // to cast the pointer to the expected value. Value *src = b_.CreateSafeGEP(b_.getInt8Ty(), scoped_src.value(), offset); if (elem_type.IsRecordTy() || elem_type.IsArrayTy()) { // For nested arrays and structs, just pass the pointer along and // dereference it later when necessary. We just need to extend lifetime // of the source pointer. return ScopedExpr(src, std::move(scoped_src)); } else if (elem_type.IsStringTy() || elem_type.IsBufferTy()) { AllocaInst *dst = b_.CreateAllocaBPF(elem_type, temp_name); if (elem_type.IsStringTy() && data_type.is_btftype) { if (src->getType()->isIntegerTy()) src = b_.CreateIntToPtr(src, dst->getType()); b_.CreateMemcpyBPF(dst, src, elem_type.GetSize()); } else { b_.CreateProbeRead(ctx_, dst, elem_type, src, loc, data_type.GetAS()); } // dst is left as is, so we need to return and bound its lifetime to the // underlying expression. Since we've finished copying, we can end the // lifetime of the `scoped_src` argument. return ScopedExpr(dst, [this, dst]() { b_.CreateLifetimeEnd(dst); }); } else { // Read data onto stack if (data_type.IsCtxAccess() || data_type.is_btftype) { // Types have already been suitably casted; just do the access. Value *expr = b_.CreateDatastructElemLoad( elem_type, src, true, data_type.GetAS()); // check context access for iter probes (required by kernel) if (data_type.IsCtxAccess() && probetype(current_attach_point_->provider) == ProbeType::iter) { llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *pred_false_block = BasicBlock::Create(module_->getContext(), "pred_false", parent); BasicBlock *pred_true_block = BasicBlock::Create(module_->getContext(), "pred_true", parent); Value *cast = b_.CreateIntCast(expr, b_.getInt64Ty(), false); Value *cmp = b_.CreateICmpEQ(cast, b_.getInt64(0), "predcond"); b_.CreateCondBr(cmp, pred_false_block, pred_true_block); b_.SetInsertPoint(pred_false_block); createRet(); b_.SetInsertPoint(pred_true_block); } // Everything should be loaded by this point, so we can drop the lifetime // of `scoped_src`. return ScopedExpr(expr); } else { AllocaInst *dst = b_.CreateAllocaBPF(elem_type, temp_name); b_.CreateProbeRead(ctx_, dst, elem_type, src, loc, data_type.GetAS()); Value *expr = b_.CreateLoad(b_.GetType(elem_type), dst); // We have completely loaded from dst, and therefore can insert an end to // its lifetime directly. b_.CreateLifetimeEnd(dst); return ScopedExpr(expr); } } } ScopedExpr CodegenLLVM::createIncDec(Unop &unop) { bool is_increment = unop.op == Operator::INCREMENT; SizedType &type = unop.expr->type; uint64_t step = type.IsPtrTy() ? type.GetPointeeTy()->GetSize() : 1; if (unop.expr->is_map) { auto &map = static_cast(*unop.expr); auto scoped_key = getMapKey(map); Value *oldval = b_.CreateMapLookupElem( ctx_, map, scoped_key.value(), unop.loc); AllocaInst *newval = b_.CreateAllocaBPF(map.type, map.ident + "_newval"); if (is_increment) b_.CreateStore(b_.CreateAdd(oldval, b_.GetIntSameSize(step, oldval)), newval); else b_.CreateStore(b_.CreateSub(oldval, b_.GetIntSameSize(step, oldval)), newval); b_.CreateMapUpdateElem( ctx_, map.ident, scoped_key.value(), newval, unop.loc); Value *value; if (unop.is_post_op) value = oldval; else value = b_.CreateLoad(b_.GetType(map.type), newval); b_.CreateLifetimeEnd(newval); return ScopedExpr(value); } else if (unop.expr->is_variable) { Variable &var = static_cast(*unop.expr); const auto &variable = getVariable(var.ident); Value *oldval = b_.CreateLoad(variable.type, variable.value); Value *newval; if (is_increment) newval = b_.CreateAdd(oldval, b_.GetIntSameSize(step, oldval)); else newval = b_.CreateSub(oldval, b_.GetIntSameSize(step, oldval)); b_.CreateStore(newval, variable.value); if (unop.is_post_op) return ScopedExpr(oldval); else return ScopedExpr(newval); } else { LOG(BUG) << "invalid expression passed to " << opstr(unop); __builtin_unreachable(); } } llvm::Function *CodegenLLVM::createMurmurHash2Func() { // The goal is to produce the following code: // // uint64_t murmur_hash_2(void *stack, uint8_t nr_stack_frames, uint64_t seed) // { // const uint64_t m = 0xc6a4a7935bd1e995LLU; // const int r = 47; // uint64_t id = seed ^ (nr_stack_frames * m); // int i = 0; // while(i < nr_stack_frames) { // uint64_t k = stack[i]; // k *= m; // k ^= k >> r; // k *= m; // id ^= k; // id *= m; // ++i; // } // return id; // } // // https://github.com/aappleby/smhasher/blob/92cf3702fcfaadc84eb7bef59825a23e0cd84f56/src/MurmurHash2.cpp auto saved_ip = b_.saveIP(); std::array args = { b_.getPtrTy(), b_.getInt8Ty(), b_.getInt64Ty() }; FunctionType *callback_type = FunctionType::get(b_.getInt64Ty(), args, false); auto *callback = llvm::Function::Create( callback_type, llvm::Function::LinkageTypes::InternalLinkage, "murmur_hash_2", module_.get()); callback->addFnAttr(Attribute::AlwaysInline); callback->setSection("helpers"); auto *bb = BasicBlock::Create(module_->getContext(), "entry", callback); b_.SetInsertPoint(bb); AllocaInst *nr_stack_frames = b_.CreateAllocaBPF(b_.getInt8Ty(), "nr_stack_frames_addr"); AllocaInst *seed_addr = b_.CreateAllocaBPF(b_.getInt64Ty(), "seed_addr"); AllocaInst *id = b_.CreateAllocaBPF(b_.getInt64Ty(), "id"); AllocaInst *i = b_.CreateAllocaBPF(b_.getInt8Ty(), "i"); AllocaInst *k = b_.CreateAllocaBPF(b_.getInt64Ty(), "k"); Value *m = b_.getInt64(0xc6a4a7935bd1e995LLU); Value *r = b_.getInt64(47); Value *stack_addr = callback->getArg(0); b_.CreateStore(callback->getArg(1), nr_stack_frames); b_.CreateStore(callback->getArg(2), seed_addr); // uint64_t id = seed ^ (len * m); b_.CreateStore( b_.CreateXor(b_.CreateLoad(b_.getInt64Ty(), seed_addr), b_.CreateMul(b_.CreateIntCast(b_.CreateLoad(b_.getInt8Ty(), nr_stack_frames), b_.getInt64Ty(), false), m)), id); // int i = 0; b_.CreateStore(b_.getInt8(0), i); llvm::Function *parent = b_.GetInsertBlock()->getParent(); BasicBlock *while_cond = BasicBlock::Create(module_->getContext(), "while_cond", parent); BasicBlock *while_body = BasicBlock::Create(module_->getContext(), "while_body", parent); BasicBlock *while_end = BasicBlock::Create(module_->getContext(), "while_end", parent); b_.CreateBr(while_cond); b_.SetInsertPoint(while_cond); auto *cond = b_.CreateICmp(CmpInst::ICMP_ULT, b_.CreateLoad(b_.getInt8Ty(), i), b_.CreateLoad(b_.getInt8Ty(), nr_stack_frames), "length.cmp"); b_.CreateCondBr(cond, while_body, while_end); b_.SetInsertPoint(while_body); // uint64_t k = stack[i]; Value *stack_ptr = b_.CreateGEP(b_.getInt64Ty(), stack_addr, b_.CreateLoad(b_.getInt8Ty(), i)); b_.CreateStore(b_.CreateLoad(b_.getInt64Ty(), stack_ptr), k); // k *= m; b_.CreateStore(b_.CreateMul(b_.CreateLoad(b_.getInt64Ty(), k), m), k); // // k ^= k >> r b_.CreateStore(b_.CreateXor(b_.CreateLoad(b_.getInt64Ty(), k), b_.CreateLShr(b_.CreateLoad(b_.getInt64Ty(), k), r)), k); // // k *= m b_.CreateStore(b_.CreateMul(b_.CreateLoad(b_.getInt64Ty(), k), m), k); // id ^= k b_.CreateStore(b_.CreateXor(b_.CreateLoad(b_.getInt64Ty(), id), b_.CreateLoad(b_.getInt64Ty(), k)), id); // id *= m b_.CreateStore(b_.CreateMul(b_.CreateLoad(b_.getInt64Ty(), id), m), id); // ++i b_.CreateStore(b_.CreateAdd(b_.CreateLoad(b_.getInt8Ty(), i), b_.getInt8(1)), i); b_.CreateBr(while_cond); b_.SetInsertPoint(while_end); b_.CreateLifetimeEnd(nr_stack_frames); b_.CreateLifetimeEnd(seed_addr); b_.CreateLifetimeEnd(i); b_.CreateLifetimeEnd(k); // We reserve 0 for errors so if we happen to hash to 0 just set to 1. // This should reduce hash collisions as we now have to come across two stacks // that naturally hash to 1 AND 0. BasicBlock *if_zero = BasicBlock::Create(module_->getContext(), "if_zero", parent); BasicBlock *if_end = BasicBlock::Create(module_->getContext(), "if_end", parent); Value *zero_cond = b_.CreateICmpEQ(b_.CreateLoad(b_.getInt64Ty(), id), b_.getInt64(0), "zero_cond"); b_.CreateCondBr(zero_cond, if_zero, if_end); b_.SetInsertPoint(if_zero); b_.CreateStore(b_.getInt64(1), id); b_.CreateBr(if_end); b_.SetInsertPoint(if_end); Value *ret = b_.CreateLoad(b_.getInt64Ty(), id); b_.CreateLifetimeEnd(id); b_.CreateRet(ret); b_.restoreIP(saved_ip); return callback; } llvm::Function *CodegenLLVM::createMapLenCallback() { // The goal is to produce the following code: // // static int cb(struct map *map, void *key, void *value, void *ctx) // { // return 0; // } auto saved_ip = b_.saveIP(); std::array args = { b_.getPtrTy(), b_.getPtrTy(), b_.getPtrTy(), b_.getPtrTy() }; FunctionType *callback_type = FunctionType::get(b_.getInt64Ty(), args, false); auto *callback = llvm::Function::Create( callback_type, llvm::Function::LinkageTypes::InternalLinkage, "map_len_cb", module_.get()); callback->setDSOLocal(true); callback->setVisibility(llvm::GlobalValue::DefaultVisibility); callback->setSection(".text"); Struct debug_args; debug_args.AddField("map", CreatePointer(CreateInt8())); debug_args.AddField("key", CreatePointer(CreateInt8())); debug_args.AddField("value", CreatePointer(CreateInt8())); debug_args.AddField("ctx", CreatePointer(CreateInt8())); debug_.createFunctionDebugInfo(*callback, CreateInt64(), debug_args); auto *bb = BasicBlock::Create(module_->getContext(), "", callback); b_.SetInsertPoint(bb); b_.CreateRet(b_.getInt64(0)); b_.restoreIP(saved_ip); return callback; } llvm::Function *CodegenLLVM::createForEachMapCallback(For &f, llvm::Type *ctx_t) { // Create a callback function suitable for passing to bpf_for_each_map_elem, // of the form: // // static int cb(struct map *map, void *key, void *value, void *ctx) // { // $decl = (key, value); // [stmts...] // } auto saved_ip = b_.saveIP(); std::array args = { b_.getPtrTy(), b_.getPtrTy(), b_.getPtrTy(), b_.getPtrTy() }; FunctionType *callback_type = FunctionType::get(b_.getInt64Ty(), args, false); auto *callback = llvm::Function::Create( callback_type, llvm::Function::LinkageTypes::InternalLinkage, "map_for_each_cb", module_.get()); callback->setDSOLocal(true); callback->setVisibility(llvm::GlobalValue::DefaultVisibility); callback->setSection(".text"); Struct debug_args; debug_args.AddField("map", CreatePointer(CreateInt8())); debug_args.AddField("key", CreatePointer(CreateInt8())); debug_args.AddField("value", CreatePointer(CreateInt8())); debug_args.AddField("ctx", CreatePointer(CreateInt8())); debug_.createFunctionDebugInfo(*callback, CreateInt64(), debug_args); auto *bb = BasicBlock::Create(module_->getContext(), "", callback); b_.SetInsertPoint(bb); auto &key_type = f.decl->type.GetField(0).type; Value *key = callback->getArg(1); if (!inBpfMemory(key_type)) { key = b_.CreateLoad(b_.GetType(key_type), key, "key"); } auto &map = static_cast(*f.expr); auto map_info = bpftrace_.resources.maps_info.find(map.ident); if (map_info == bpftrace_.resources.maps_info.end()) { LOG(BUG) << "map name: \"" << map.ident << "\" not found"; } auto &val_type = f.decl->type.GetField(1).type; Value *val = callback->getArg(2); const auto &map_val_type = map_info->second.value_type; if (canAggPerCpuMapElems(map_val_type, map_info->second.key_type)) { val = b_.CreatePerCpuMapAggElems( ctx_, map, callback->getArg(1), map_val_type, map.loc); } else if (!inBpfMemory(val_type)) { val = b_.CreateLoad(b_.GetType(val_type), val, "val"); } // Create decl variable for use in this iteration of the loop auto tuple = createTuple(f.decl->type, { { key, &f.decl->loc }, { val, &f.decl->loc } }, f.decl->ident, f.decl->loc); variables_[scope_stack_.back()][f.decl->ident] = VariableLLVM{ tuple, b_.GetType(f.decl->type) }; // 1. Save original locations of variables which will form part of the // callback context // 2. Replace variable expressions with those from the context Value *ctx = callback->getArg(3); const auto &ctx_fields = f.ctx_type.GetFields(); std::unordered_map orig_ctx_vars; for (size_t i = 0; i < ctx_fields.size(); i++) { const auto &field = ctx_fields[i]; orig_ctx_vars[field.name] = getVariable(field.name).value; auto *ctx_field_ptr = b_.CreateGEP( ctx_t, ctx, { b_.getInt64(0), b_.getInt32(i) }, "ctx." + field.name); getVariable(field.name).value = b_.CreateLoad(b_.getPtrTy(), ctx_field_ptr, field.name); } // Generate code for the loop body visit(f.stmts); b_.CreateRet(b_.getInt64(0)); // Restore original non-context variables for (const auto &[ident, expr] : orig_ctx_vars) { getVariable(ident).value = expr; } // Decl variable is not valid beyond this for loop variables_[scope_stack_.back()].erase(f.decl->ident); b_.restoreIP(saved_ip); return callback; } bool CodegenLLVM::canAggPerCpuMapElems(const SizedType &val_type, const SizedType &key_type) { auto map_type = get_map_type(val_type, key_type); return val_type.IsCastableMapTy() && (map_type == libbpf::BPF_MAP_TYPE_PERCPU_ARRAY || map_type == libbpf::BPF_MAP_TYPE_PERCPU_HASH); } // BPF helpers that use fmt strings (bpf_trace_printk, bpf_seq_printf) expect // the string passed in a data map. libbpf is able to create the map internally // if an internal global constant string is used. This function creates the // constant. Uses bpf_print_id_ to pick the correct format string from // RequiredResources. Value *CodegenLLVM::createFmtString(int print_id) { auto fmt_str = bpftrace_.resources.bpf_print_fmts.at(print_id); auto res = llvm::dyn_cast(module_->getOrInsertGlobal( "__fmt_" + std::to_string(print_id), ArrayType::get(b_.getInt8Ty(), fmt_str.length() + 1))); res->setConstant(true); res->setInitializer( ConstantDataArray::getString(module_->getContext(), fmt_str.c_str())); res->setAlignment(MaybeAlign(1)); res->setLinkage(llvm::GlobalValue::InternalLinkage); return res; } /// This should emit /// /// declare !dbg !... extern_weak ... @func_name(...) section ".ksyms" /// /// with proper debug info entry. /// /// The function type is retrieved from kernel BTF. /// /// If the function declaration is already in the module, just return it. /// llvm::Function *CodegenLLVM::DeclareKernelFunc(Kfunc kfunc) { const std::string &func_name = kfunc_name(kfunc); if (auto *fun = module_->getFunction(func_name)) return fun; std::string err; auto maybe_func_type = bpftrace_.btf_->resolve_args(func_name, true, err); if (!maybe_func_type.has_value()) { throw FatalUserException(err); } std::vector args; for (auto &field : maybe_func_type->fields) { if (field.name != RETVAL_FIELD_NAME) args.push_back(b_.GetType(field.type, false)); } FunctionType *func_type = FunctionType::get( b_.GetType(maybe_func_type->GetField(RETVAL_FIELD_NAME).type, false), args, false); auto *fun = llvm::Function::Create(func_type, llvm::GlobalValue::ExternalWeakLinkage, func_name, module_.get()); fun->setSection(".ksyms"); fun->setUnnamedAddr(GlobalValue::UnnamedAddr::Local); // Copy args and remove the last field (retval) as we pass it to // createFunctionDebugInfo separately Struct debug_args = *maybe_func_type; // copy here debug_args.fields.pop_back(); debug_.createFunctionDebugInfo( *fun, maybe_func_type->GetField(RETVAL_FIELD_NAME).type, debug_args, true); return fun; } CallInst *CodegenLLVM::CreateKernelFuncCall(Kfunc kfunc, ArrayRef args, const Twine &name) { auto func = DeclareKernelFunc(kfunc); return b_.createCall(func->getFunctionType(), func, args, name); } /// This should emit /// /// declare !dbg !... extern ... @var_name(...) section ".ksyms" /// /// with proper debug info entry. /// /// The function type is retrieved from kernel BTF. /// /// If the function declaration is already in the module, just return it. /// GlobalVariable *CodegenLLVM::DeclareKernelVar(const std::string &var_name) { if (auto *sym = module_->getGlobalVariable(var_name)) return sym; std::string err; auto type = bpftrace_.btf_->get_var_type(var_name); assert(!type.IsNoneTy()); // already checked in semantic analyser auto var = llvm::dyn_cast( module_->getOrInsertGlobal(var_name, b_.GetType(type))); var->setSection(".ksyms"); var->setLinkage(llvm::GlobalValue::ExternalLinkage); auto var_debug = debug_.createGlobalVariable(var_name, type); var->addDebugInfo(var_debug); return var; } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/codegen_llvm.h000066400000000000000000000301241477746507000210700ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "ast/async_ids.h" #include "ast/dibuilderbpf.h" #include "ast/irbuilderbpf.h" #include "ast/visitor.h" #include "bpftrace.h" #include "codegen_resources.h" #include "format_string.h" #include "kfuncs.h" #include "location.hh" #include "required_resources.h" namespace bpftrace { namespace ast { using namespace llvm; using CallArgs = std::vector>>; struct VariableLLVM { llvm::Value *value; llvm::Type *type; }; // ScopedExpr ties an SSA value to a "delete" function, that typically will end // the lifetime of some needed storage. You must explicitly construct a // ScopedExpr from either: // * A value only, with no associated function when out of scope. // * A value and associated function to run when out of scope. // * A value and another ScopedExpr, whose lifetime will be preserved until // this value is out of scope. class ScopedExpr { public: // Neither a value nor a deletion method. explicit ScopedExpr() { } // Value only. explicit ScopedExpr(Value *value) : value_(value) { } // Value with an explicit deletion method. explicit ScopedExpr(Value *value, llvm::unique_function &&deleter) : value_(value), deleter_(std::move(deleter)) { } // Value with another ScopedExpr whose lifetime should be bound. explicit ScopedExpr(Value *value, ScopedExpr &&other) : value_(value) { deleter_.swap(other.deleter_); } ScopedExpr(ScopedExpr &&other) : value_(other.value_) { deleter_.swap(other.deleter_); } ScopedExpr &operator=(ScopedExpr &&other) { value_ = other.value_; deleter_.swap(other.deleter_); return *this; } ScopedExpr(const ScopedExpr &other) = delete; ScopedExpr &operator=(const ScopedExpr &other) = delete; ~ScopedExpr() { if (deleter_) { deleter_.value()(); deleter_.reset(); } } Value *value() { return value_; } // May be used to disable the deletion method, essentially leaking some // memory within the frame. The use of this function should be generally // considered a bug, as it will make dealing with larger functions and // multiple scopes more problematic over time. void disarm() { deleter_.reset(); } private: Value *value_ = nullptr; std::optional> deleter_; }; class CodegenLLVM : public Visitor { public: explicit CodegenLLVM(ASTContext &ctx, BPFtrace &bpftrace); explicit CodegenLLVM(ASTContext &ctx, BPFtrace &bpftrace, std::unique_ptr usdt_helper); using Visitor::visit; ScopedExpr visit(Integer &integer); ScopedExpr visit(PositionalParameter ¶m); ScopedExpr visit(String &string); ScopedExpr visit(Identifier &identifier); ScopedExpr visit(Builtin &builtin); ScopedExpr visit(Call &call); ScopedExpr visit(Sizeof &szof); ScopedExpr visit(Offsetof &offof); ScopedExpr visit(Map &map); ScopedExpr visit(Variable &var); ScopedExpr visit(Binop &binop); ScopedExpr visit(Unop &unop); ScopedExpr visit(Ternary &ternary); ScopedExpr visit(FieldAccess &acc); ScopedExpr visit(ArrayAccess &arr); ScopedExpr visit(Cast &cast); ScopedExpr visit(Tuple &tuple); ScopedExpr visit(ExprStatement &expr); ScopedExpr visit(AssignMapStatement &assignment); ScopedExpr visit(AssignVarStatement &assignment); ScopedExpr visit(VarDeclStatement &decl); ScopedExpr visit(If &if_node); ScopedExpr visit(Unroll &unroll); ScopedExpr visit(While &while_block); ScopedExpr visit(For &f); ScopedExpr visit(Jump &jump); ScopedExpr visit(Predicate &pred); ScopedExpr visit(AttachPoint &ap); ScopedExpr visit(Probe &probe); ScopedExpr visit(Subprog &subprog); ScopedExpr visit(Program &program); ScopedExpr visit(Block &block); ScopedExpr getHistMapKey(Map &map, Value *log2, const location &loc); int getNextIndexForProbe(); ScopedExpr createLogicalAnd(Binop &binop); ScopedExpr createLogicalOr(Binop &binop); // 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, const CallArgs &call_args, const std::string &call_name, AsyncAction async_action); void createPrintMapCall(Call &call); void createPrintNonMapCall(Call &call, int id); void createMapDefinition(const std::string &name, libbpf::bpf_map_type map_type, uint64_t max_entries, const SizedType &key_type, const SizedType &value_type); Value *createTuple( const SizedType &tuple_type, const std::vector> &vals, const std::string &name, const location &loc); void createTupleCopy(const SizedType &expr_type, const SizedType &var_type, Value *dst_val, Value *src_val); void generate_ir(void); libbpf::bpf_map_type get_map_type(const SizedType &val_type, const SizedType &key_type); bool is_array_map(const SizedType &val_type, const SizedType &key_type); bool map_has_single_elem(const SizedType &val_type, const SizedType &key_type); void generate_maps(const RequiredResources &rr, const CodegenResources &cr); void generate_global_vars(const RequiredResources &resources, const ::bpftrace::Config &bpftrace_config); void optimize(void); bool verify(void); BpfBytecode emit(bool disassemble); void emit_elf(const std::string &filename); void emit(raw_pwrite_stream &stream); // Combine generate_ir, optimize and emit into one call BpfBytecode compile(void); private: static constexpr char LLVMTargetTriple[] = "bpf-pc-linux"; // Generate a probe for `current_attach_point_` // // If `dummy` is passed, then code is generated but immediately thrown away. // This is used to progress state (eg. asyncids) in this class instance for // invalid probes that still need to be visited. void generateProbe(Probe &probe, const std::string &full_func_id, const std::string &name, FunctionType *func_type, std::optional usdt_location_index = std::nullopt, bool dummy = false); // Generate a probe and register it to the BPFtrace class. void add_probe(AttachPoint &ap, Probe &probe, const std::string &name, FunctionType *func_type); [[nodiscard]] ScopedExpr getMapKey(Map &map); [[nodiscard]] ScopedExpr getMapKey(Map &map, Expression *key_expr); [[nodiscard]] ScopedExpr getMultiMapKey( Map &map, const std::vector &extra_keys, const location &loc); void compareStructure(SizedType &our_type, llvm::Type *llvm_type); llvm::Function *createLog2Function(); llvm::Function *createLinearFunction(); MDNode *createLoopMetadata(); std::pair getString(Expression &expr); ScopedExpr binop_string(Binop &binop); ScopedExpr binop_integer_array(Binop &binop); ScopedExpr binop_buf(Binop &binop); ScopedExpr binop_int(Binop &binop); ScopedExpr binop_ptr(Binop &binop); ScopedExpr unop_int(Unop &unop); ScopedExpr unop_ptr(Unop &unop); ScopedExpr kstack_ustack(const std::string &ident, StackType stack_type, const location &loc); int get_probe_id(); // Create return instruction // // If null, return value will depend on current attach point (void in subprog) void createRet(Value *value = nullptr); int getReturnValueForProbe(ProbeType probe_type); // Every time we see a watchpoint that specifies a function + arg pair, we // generate a special "setup" probe that: // // * sends SIGSTOP to the tracee // * pulls out the function arg // * sends an asyncaction to the bpftrace runtime and specifies the arg value // and which of the "real" probes to attach to the addr in the arg // // We need a separate "setup" probe per probe because we hard code the index // of the "real" probe the setup probe is to be replaced by. void generateWatchpointSetupProbe(FunctionType *func_type, const std::string &expanded_probe_name, int arg_num, int index); ScopedExpr readDatastructElemFromStack(ScopedExpr &&scoped_src, Value *index, const SizedType &data_type, const SizedType &elem_type); ScopedExpr readDatastructElemFromStack(ScopedExpr &&scoped_src, Value *index, llvm::Type *data_type, const SizedType &elem_type); ScopedExpr probereadDatastructElem(ScopedExpr &&scoped_src, Value *offset, const SizedType &data_type, const SizedType &elem_type, location loc, const std::string &temp_name); ScopedExpr createIncDec(Unop &unop); llvm::Function *createMapLenCallback(); llvm::Function *createForEachMapCallback(For &f, llvm::Type *ctx_t); llvm::Function *createMurmurHash2Func(); Value *createFmtString(int print_id); bool canAggPerCpuMapElems(const SizedType &val_type, const SizedType &key_type); void maybeAllocVariable(const std::string &var_ident, const SizedType &var_type, const location &loc); VariableLLVM *maybeGetVariable(const std::string &); VariableLLVM &getVariable(const std::string &); llvm::Function *DeclareKernelFunc(Kfunc kfunc); CallInst *CreateKernelFuncCall(Kfunc kfunc, ArrayRef args, const Twine &name); GlobalVariable *DeclareKernelVar(const std::string &name); BPFtrace &bpftrace_; std::unique_ptr usdt_helper_; std::unique_ptr context_; std::unique_ptr target_machine_; std::unique_ptr module_; AsyncIds async_ids_; IRBuilderBPF b_; DIBuilderBPF debug_; const DataLayout &datalayout() const { return module_->getDataLayout(); } Value *ctx_; AttachPoint *current_attach_point_ = nullptr; std::string probefull_; std::string tracepoint_struct_; uint64_t probe_count_ = 0; // Probes and attach points are indexed from 1, 0 means no index // (no index is used for probes whose attach points are indexed individually) int next_probe_index_ = 1; // Used if there are duplicate USDT entries int current_usdt_location_index_{ 0 }; bool inside_subprog_ = false; std::vector scope_stack_; std::unordered_map> variables_; std::unordered_map map_types_; llvm::Function *linear_func_ = nullptr; llvm::Function *log2_func_ = nullptr; llvm::Function *murmur_hash_2_func_ = nullptr; llvm::Function *map_len_func_ = nullptr; MDNode *loop_metadata_ = nullptr; size_t getStructSize(StructType *s) { return module_->getDataLayout().getTypeAllocSize(s); } std::vector> loops_; std::unordered_map probe_names_; enum class State { INIT, IR, OPT, DONE, }; State state_ = State::INIT; }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/passes/codegen_resources.cpp000066400000000000000000000020151477746507000224610ustar00rootroot00000000000000#include "codegen_resources.h" #include "ast/async_event_types.h" #include "struct.h" #include "types.h" namespace bpftrace::ast { CodegenResourceAnalyser::CodegenResourceAnalyser( ASTContext &ctx, const ::bpftrace::Config &config) : Visitor(ctx), config_(config) { } CodegenResources CodegenResourceAnalyser::analyse() { visit(ctx_.root); return std::move(resources_); } void CodegenResourceAnalyser::visit(Builtin &builtin) { if (builtin.ident == "elapsed") { resources_.needs_elapsed_map = true; } else if (builtin.ident == "kstack" || builtin.ident == "ustack") { resources_.stackid_maps.insert( StackType{ .mode = config_.get(ConfigKeyStackMode::default_) }); } } void CodegenResourceAnalyser::visit(Call &call) { Visitor::visit(call); if (call.func == "join") { resources_.needs_join_map = true; } else if (call.func == "kstack" || call.func == "ustack") { resources_.stackid_maps.insert(call.type.stack_type); } } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/codegen_resources.h000066400000000000000000000016571477746507000221410ustar00rootroot00000000000000#pragma once #include #include #include "ast/visitor.h" #include "config.h" namespace bpftrace { namespace ast { struct CodegenResources { bool needs_elapsed_map = false; bool needs_join_map = false; std::unordered_set stackid_maps; }; // Codegen resource analysis pass // // This pass collects specific information codegen later needs. All this // could be done in codegen pass itself, but splitting out some "prerun" // logic makes things easier to understand and maintain. class CodegenResourceAnalyser : public Visitor { public: CodegenResourceAnalyser(ASTContext &ctx, const ::bpftrace::Config &config); CodegenResources analyse(); using Visitor::visit; void visit(Builtin &map); void visit(Call &call); private: const ::bpftrace::Config &config_; CodegenResources resources_; }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/passes/collect_nodes.h000066400000000000000000000020121477746507000212420ustar00rootroot00000000000000#pragma once #include #include #include "ast/visitor.h" namespace bpftrace::ast { // CollectNodes // // Recurses into the provided node and builds a list of all descendants of the // requested type which match a predicate. template class CollectNodes : public Visitor> { public: explicit CollectNodes(ASTContext &ctx) : Visitor>(ctx), pred_([](const auto &) { return true; }) { } const std::vector> &nodes() const { return nodes_; } using Visitor>::visit; void visit(NodeT &node) { if (pred_(node)) { nodes_.push_back(node); } Visitor>::visit(node); } template void visit(T &node, std::function pred) { pred_ = pred; visit(node); } private: std::vector> nodes_; std::function pred_; }; } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/config_analyser.cpp000066400000000000000000000122051477746507000221300ustar00rootroot00000000000000#include "config_analyser.h" #include #include #include "ast/ast.h" #include "config.h" #include "log.h" #include "types.h" namespace bpftrace::ast { void ConfigAnalyser::log_type_error(SizedType &type, Type expected_type, AssignConfigVarStatement &assignment) { LOG(ERROR, assignment.loc, err_) << "Invalid type for " << assignment.config_var << ". Type: " << type.GetTy() << ". Expected Type: " << expected_type; } void ConfigAnalyser::set_config(AssignConfigVarStatement &assignment, ConfigKeyInt key) { auto &assignTy = assignment.expr->type; if (!assignTy.IsIntegerTy()) { log_type_error(assignTy, Type::integer, assignment); return; } config_setter_.set(key, dynamic_cast(assignment.expr)->n); } void ConfigAnalyser::set_config(AssignConfigVarStatement &assignment, ConfigKeyBool key) { auto &assignTy = assignment.expr->type; if (!assignTy.IsIntegerTy()) { log_type_error(assignTy, Type::integer, assignment); return; } auto val = dynamic_cast(assignment.expr)->n; if (val == 0) { config_setter_.set(key, false); } else if (val == 1) { config_setter_.set(key, true); } else { LOG(ERROR) << "Invalid value for " << assignment.config_var << ". Needs to be 0 or 1. Value: " << val; } } void ConfigAnalyser::set_config(AssignConfigVarStatement &assignment, [[maybe_unused]] ConfigKeyString key) { auto &assignTy = assignment.expr->type; if (!assignTy.IsStringTy()) { log_type_error(assignTy, Type::string, assignment); return; } config_setter_.set(key, dynamic_cast(assignment.expr)->str); } void ConfigAnalyser::set_config(AssignConfigVarStatement &assignment, [[maybe_unused]] ConfigKeyStackMode) { auto &assignTy = assignment.expr->type; if (!assignTy.IsStackModeTy()) { log_type_error(assignTy, Type::stack_mode, assignment); return; } config_setter_.set(assignTy.stack_type.mode); } void ConfigAnalyser::set_config( AssignConfigVarStatement &assignment, [[maybe_unused]] ConfigKeyUserSymbolCacheType key) { auto &assignTy = assignment.expr->type; if (!assignTy.IsStringTy()) { log_type_error(assignTy, Type::string, assignment); return; } auto val = dynamic_cast(assignment.expr)->str; if (!config_setter_.set_user_symbol_cache_type(val)) LOG(ERROR, assignment.expr->loc, err_); } void ConfigAnalyser::set_config(AssignConfigVarStatement &assignment, [[maybe_unused]] ConfigKeySymbolSource key) { auto &assignTy = assignment.expr->type; if (!assignTy.IsStringTy()) { log_type_error(assignTy, Type::string, assignment); return; } auto val = dynamic_cast(assignment.expr)->str; if (!config_setter_.set_symbol_source_config(val)) LOG(ERROR, assignment.expr->loc, err_); } void ConfigAnalyser::set_config(AssignConfigVarStatement &assignment, [[maybe_unused]] ConfigKeyMissingProbes key) { auto &assignTy = assignment.expr->type; if (!assignTy.IsStringTy()) { log_type_error(assignTy, Type::string, assignment); return; } auto val = dynamic_cast(assignment.expr)->str; if (!config_setter_.set_missing_probes_config(val)) LOG(ERROR, assignment.expr->loc, err_); } void ConfigAnalyser::visit(Integer &integer) { integer.type = CreateInt64(); } void ConfigAnalyser::visit(String &string) { string.type = CreateString(string.str.size() + 1); } void ConfigAnalyser::visit(StackMode &mode) { auto stack_mode = bpftrace::Config::get_stack_mode(mode.mode); if (stack_mode.has_value()) { mode.type = CreateStackMode(); mode.type.stack_type.mode = stack_mode.value(); } else { mode.type = CreateNone(); LOG(ERROR, mode.loc, err_) << "Unknown stack mode: '" + mode.mode + "'"; } } void ConfigAnalyser::visit(AssignConfigVarStatement &assignment) { Visitor::visit(assignment); std::string &raw_ident = assignment.config_var; std::string err_msg; const auto maybeConfigKey = bpftrace_.config_.get_config_key(raw_ident, err_msg); if (!maybeConfigKey.has_value()) { LOG(ERROR, assignment.loc, err_) << err_msg; return; } if (!assignment.expr->is_literal) { LOG(ERROR, assignment.loc, err_) << "Assignment for " << assignment.config_var << " must be literal."; return; } auto configKey = maybeConfigKey.value(); std::visit([&](auto key) { set_config(assignment, key); }, configKey); } bool ConfigAnalyser::analyse() { visit(ctx_.root); std::string errors = err_.str(); if (!errors.empty()) { out_ << errors; return false; } return true; } Pass CreateConfigPass() { auto fn = [](PassContext &ctx) { auto configs = ConfigAnalyser(ctx.ast_ctx, ctx.b); if (!configs.analyse()) return PassResult::Error("Config"); return PassResult::Success(); }; return Pass("ConfigAnalyser", fn); }; } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/config_analyser.h000066400000000000000000000034051477746507000215770ustar00rootroot00000000000000#pragma once #include #include #include #include "ast/pass_manager.h" #include "ast/visitor.h" #include "bpffeature.h" #include "bpftrace.h" #include "config.h" #include "types.h" namespace bpftrace { namespace ast { class ConfigAnalyser : public Visitor { public: explicit ConfigAnalyser(ASTContext &ctx, BPFtrace &bpftrace, std::ostream &out = std::cerr) : Visitor(ctx), bpftrace_(bpftrace), config_setter_(ConfigSetter(bpftrace.config_, ConfigSource::script)), out_(out) { } using Visitor::visit; void visit(Integer &integer); void visit(String &string); void visit(StackMode &mode); void visit(AssignConfigVarStatement &assignment); bool analyse(); private: BPFtrace &bpftrace_; ConfigSetter config_setter_; std::ostream &out_; std::ostringstream err_; void set_config(AssignConfigVarStatement &assignment, ConfigKeyInt key); void set_config(AssignConfigVarStatement &assignment, ConfigKeyBool key); void set_config(AssignConfigVarStatement &assignment, ConfigKeyString key); void set_config(AssignConfigVarStatement &assignment, ConfigKeyUserSymbolCacheType key); void set_config(AssignConfigVarStatement &assignment, ConfigKeySymbolSource key); void set_config(AssignConfigVarStatement &assignment, ConfigKeyStackMode key); void set_config(AssignConfigVarStatement &assignment, ConfigKeyMissingProbes key); void log_type_error(SizedType &type, Type expected_type, AssignConfigVarStatement &assignment); }; Pass CreateConfigPass(); } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/passes/field_analyser.cpp000066400000000000000000000221561477746507000217540ustar00rootroot00000000000000#include "field_analyser.h" #include #include #include "arch/arch.h" #include "dwarf_parser.h" #include "log.h" #include "probe_matcher.h" namespace bpftrace::ast { void FieldAnalyser::visit(Identifier &identifier) { bpftrace_.btf_set_.insert(identifier.ident); } void FieldAnalyser::visit(Builtin &builtin) { std::string builtin_type; sized_type_ = CreateNone(); if (builtin.ident == "ctx") { if (!probe_) return; switch (prog_type_) { case libbpf::BPF_PROG_TYPE_KPROBE: builtin_type = "struct pt_regs"; break; case libbpf::BPF_PROG_TYPE_PERF_EVENT: builtin_type = "struct bpf_perf_event_data"; break; default: break; } // For each iterator probe, the context is pointing to specific struct, // make them resolved and available if (probe_type_ == ProbeType::iter) builtin_type = "struct bpf_iter__" + attach_func_; } else if (builtin.ident == "curtask") { builtin_type = "struct task_struct"; } else if (builtin.ident == "args") { if (!probe_) return; resolve_args(*probe_); has_builtin_args_ = true; return; } else if (builtin.ident == "retval") { if (!probe_) return; resolve_args(*probe_); auto arg = bpftrace_.structs.GetProbeArg(*probe_, RETVAL_FIELD_NAME); if (arg) sized_type_ = arg->type; return; } if (bpftrace_.has_btf_data()) sized_type_ = bpftrace_.btf_->get_stype(builtin_type); } void FieldAnalyser::visit(Map &map) { visit(map.key_expr); auto it = var_types_.find(map.ident); if (it != var_types_.end()) sized_type_ = it->second; } void FieldAnalyser::visit(Variable &var) { auto it = var_types_.find(var.ident); if (it != var_types_.end()) sized_type_ = it->second; } void FieldAnalyser::visit(FieldAccess &acc) { has_builtin_args_ = false; visit(acc.expr); if (has_builtin_args_) { auto arg = bpftrace_.structs.GetProbeArg(*probe_, acc.field); if (arg) sized_type_ = arg->type; has_builtin_args_ = false; } else if (sized_type_.IsRecordTy()) { SizedType field_type = CreateNone(); if (sized_type_.HasField(acc.field)) field_type = sized_type_.GetField(acc.field).type; if (!field_type.IsNoneTy()) { sized_type_ = field_type; } else if (bpftrace_.has_btf_data()) { // If the struct type or the field type has not been resolved, add the // type to the BTF set to let ClangParser resolve it bpftrace_.btf_set_.insert(sized_type_.GetName()); auto field_type_name = bpftrace_.btf_->type_of(sized_type_.GetName(), acc.field); bpftrace_.btf_set_.insert(field_type_name); } } } void FieldAnalyser::visit(ArrayAccess &arr) { visit(arr.indexpr); visit(arr.expr); if (sized_type_.IsPtrTy()) { sized_type_ = *sized_type_.GetPointeeTy(); resolve_fields(sized_type_); } else if (sized_type_.IsArrayTy()) { sized_type_ = *sized_type_.GetElementTy(); resolve_fields(sized_type_); } } void FieldAnalyser::visit(Cast &cast) { visit(cast.expr); resolve_type(cast.type); } void FieldAnalyser::visit(Sizeof &szof) { visit(szof.expr); resolve_type(szof.argtype); } void FieldAnalyser::visit(Offsetof &offof) { if (offof.expr) visit(*offof.expr); resolve_type(offof.record); } void FieldAnalyser::visit(AssignMapStatement &assignment) { visit(assignment.map); visit(assignment.expr); var_types_.emplace(assignment.map->ident, sized_type_); } void FieldAnalyser::visit(AssignVarStatement &assignment) { visit(assignment.expr); var_types_.emplace(assignment.var->ident, sized_type_); } void FieldAnalyser::visit(Unop &unop) { visit(unop.expr); if (unop.op == Operator::MUL && sized_type_.IsPtrTy()) { // Need a temporary to prevent UAF from self-referential assignment auto tmp = *sized_type_.GetPointeeTy(); sized_type_ = std::move(tmp); resolve_fields(sized_type_); } } void FieldAnalyser::resolve_args(Probe &probe) { for (auto *ap : probe.attach_points) { // load probe arguments into a special record type "struct _args" Struct probe_args; auto probe_type = probetype(ap->provider); if (probe_type != ProbeType::fentry && probe_type != ProbeType::fexit && probe_type != ProbeType::uprobe) continue; if (ap->expansion != ExpansionType::NONE) { std::set matches; // Find all the matches for the wildcard.. try { matches = bpftrace_.probe_matcher_->get_matches_for_ap(*ap); } catch (const WildcardException &e) { LOG(ERROR) << e.what(); return; } // ... and check if they share same arguments. Struct ap_args; for (auto &match : matches) { // Both uprobes and fentry have a target (binary for uprobes, kernel // module for fentry). std::string func = match; std::string target = erase_prefix(func); // Trying to attach to multiple fentry. If some of them fails on // argument resolution, do not fail hard, just print a warning and // continue with other functions. if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit) { std::string err; auto maybe_ap_args = bpftrace_.btf_->resolve_args( func, probe_type == ProbeType::fexit, err); if (!maybe_ap_args.has_value()) { LOG(WARNING) << "fentry:" << ap->func << ": " << err; continue; } ap_args = std::move(*maybe_ap_args); } else // uprobe { Dwarf *dwarf = bpftrace_.get_dwarf(target); if (dwarf) ap_args = dwarf->resolve_args(func); else LOG(WARNING, ap->loc, err_) << "No debuginfo found for " << target; } if (probe_args.size == -1) probe_args = ap_args; else if (ap_args != probe_args) { LOG(ERROR, ap->loc, err_) << "Probe has attach points with mixed arguments"; break; } } } else { // Resolving args for an explicit function failed, print an error and fail if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit) { std::string err; auto maybe_probe_args = bpftrace_.btf_->resolve_args( ap->func, probe_type == ProbeType::fexit, err); if (!maybe_probe_args.has_value()) { LOG(ERROR, ap->loc, err_) << "fentry:" << ap->func << ": " << err; return; } probe_args = std::move(*maybe_probe_args); } else // uprobe { Dwarf *dwarf = bpftrace_.get_dwarf(ap->target); if (dwarf) probe_args = dwarf->resolve_args(ap->func); else { LOG(WARNING, ap->loc, err_) << "No debuginfo found for " << ap->target; } if (static_cast(probe_args.fields.size()) > (arch::max_arg() + 1)) { LOG(ERROR, ap->loc, err_) << "\'args\' builtin is not supported for " "probes with stack-passed arguments."; } } } // check if we already stored arguments for this probe auto args = bpftrace_.structs.Lookup(probe.args_typename()).lock(); if (args && *args != probe_args) { // we did, and it's different...trigger the error LOG(ERROR, ap->loc, err_) << "Probe has attach points with mixed arguments"; } else { // store/save args for each ap for later processing bpftrace_.structs.Add(probe.args_typename(), std::move(probe_args)); } } return; } void FieldAnalyser::resolve_fields(SizedType &type) { if (!type.IsRecordTy()) return; if (probe_) { for (auto &ap : probe_->attach_points) if (Dwarf *dwarf = bpftrace_.get_dwarf(*ap)) dwarf->resolve_fields(type); } if (type.GetFieldCount() == 0 && bpftrace_.has_btf_data()) bpftrace_.btf_->resolve_fields(type); } void FieldAnalyser::resolve_type(SizedType &type) { sized_type_ = CreateNone(); const SizedType *inner_type = &type; while (inner_type->IsPtrTy()) inner_type = inner_type->GetPointeeTy(); if (!inner_type->IsRecordTy()) return; auto name = inner_type->GetName(); if (probe_) { for (auto &ap : probe_->attach_points) if (Dwarf *dwarf = bpftrace_.get_dwarf(*ap)) sized_type_ = dwarf->get_stype(name); } if (sized_type_.IsNoneTy() && bpftrace_.has_btf_data()) sized_type_ = bpftrace_.btf_->get_stype(name); // Could not resolve destination type - let ClangParser do it if (sized_type_.IsNoneTy()) bpftrace_.btf_set_.insert(name); } void FieldAnalyser::visit(Probe &probe) { probe_ = &probe; for (AttachPoint *ap : probe.attach_points) { probe_type_ = probetype(ap->provider); prog_type_ = progtype(probe_type_); attach_func_ = ap->func; } if (probe.pred) { visit(probe.pred); } visit(probe.block); } void FieldAnalyser::visit(Subprog &subprog) { probe_ = nullptr; visit(subprog.stmts); } int FieldAnalyser::analyse() { visit(ctx_.root); std::string errors = err_.str(); if (!errors.empty()) { out_ << errors; return 1; } return 0; } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/field_analyser.h000066400000000000000000000030101477746507000214050ustar00rootroot00000000000000#pragma once #include #include #include #include "ast/visitor.h" #include "bpftrace.h" namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace { namespace ast { class FieldAnalyser : public Visitor { public: explicit FieldAnalyser(ASTContext &ctx, BPFtrace &bpftrace, std::ostream &out = std::cerr) : Visitor(ctx), bpftrace_(bpftrace), prog_type_(libbpf::BPF_PROG_TYPE_UNSPEC), out_(out) { } using Visitor::visit; void visit(Identifier &identifier); void visit(Builtin &builtin); void visit(Map &map); void visit(Variable &var); void visit(FieldAccess &acc); void visit(ArrayAccess &arr); void visit(Cast &cast); void visit(Sizeof &szof); void visit(Offsetof &offof); void visit(AssignMapStatement &assignment); void visit(AssignVarStatement &assignment); void visit(Unop &unop); void visit(Probe &probe); void visit(Subprog &subprog); int analyse(); private: void resolve_args(Probe &probe); void resolve_fields(SizedType &type); void resolve_type(SizedType &type); ProbeType probe_type_; std::string attach_func_; SizedType sized_type_; BPFtrace &bpftrace_; libbpf::bpf_prog_type prog_type_; bool has_builtin_args_; Probe *probe_ = nullptr; std::ostream &out_; std::ostringstream err_; std::map var_types_; }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/passes/portability_analyser.cpp000066400000000000000000000104061477746507000232260ustar00rootroot00000000000000#include "portability_analyser.h" #include #include "log.h" #include "types.h" namespace bpftrace::ast { PortabilityAnalyser::PortabilityAnalyser(ASTContext &ctx, std::ostream &out) : Visitor(ctx), out_(out) { } int PortabilityAnalyser::analyse() { visit(ctx_.root); std::string errors = err_.str(); if (!errors.empty()) { out_ << errors; return 1; } return 0; } void PortabilityAnalyser::visit(PositionalParameter ¶m) { // Positional params are only known at runtime. Currently, codegen directly // embeds positional params into the bytecode but that does not work for AOT. // // In theory we could allow positional params for AOT and just embed the // values into the bytecode but there's really no point to that as: // // * that would mislead the user into thinking there's positional param // support // * the user can just hard code the values into their script LOG(ERROR, param.loc, err_) << "AOT does not yet support positional parameters"; } void PortabilityAnalyser::visit(Builtin &builtin) { // `struct task_struct` is unstable across kernel versions and configurations. // This makes it inherently unportable. We must block it until we support // field access relocations. if (builtin.ident == "curtask") { LOG(ERROR, builtin.loc, err_) << "AOT does not yet support accessing `curtask`"; } } void PortabilityAnalyser::visit(Call &call) { for (Expression *expr : call.vargs) visit(*expr); // kaddr() and uaddr() both resolve symbols -> address during codegen and // embeds the values into the bytecode. For AOT to support kaddr()/uaddr(), // the addresses must be resolved at runtime and fixed up during load time. // // cgroupid can vary across systems just like how a process does not // necessarily share the same PID across multiple systems. cgroupid() is also // resolved during codegen and the value embedded into the bytecode. For AOT // to support cgroupid(), the cgroupid must be resolved at runtime and fixed // up during load time. if (call.func == "kaddr" || call.func == "uaddr" || call.func == "cgroupid") { LOG(ERROR, call.loc, err_) << "AOT does not yet support " << call.func << "()"; } } void PortabilityAnalyser::visit(Cast &cast) { visit(*cast.expr); // The goal here is to block arbitrary field accesses but still allow `args` // access. `args` for tracepoint is fairly stable and should be considered // portable. `args` for k[ret]funcs are type checked by the kernel and may // also be considered stable. For AOT to fully support field accesses, we // need to relocate field access at runtime. LOG(ERROR, cast.loc, err_) << "AOT does not yet support struct casts"; } void PortabilityAnalyser::visit(AttachPoint &ap) { auto type = probetype(ap.provider); // USDT probes require analyzing a USDT enabled binary for precise offsets // and argument information. This analyzing is currently done during codegen // and offsets and type information is embedded into the bytecode. For AOT // support, this analyzing must be done during runtime and fixed up during // load time. if (type == ProbeType::usdt) { LOG(ERROR, ap.loc, err_) << "AOT does not yet support USDT probes"; } // While userspace watchpoint probes are technically portable from codegen // point of view, they require a PID or path via cmdline to resolve address. // watchpoint probes are also API-unstable and need a further change // (see https://github.com/bpftrace/bpftrace/issues/1683). // // So disable for now and re-evalulate at another point. else if (type == ProbeType::watchpoint || type == ProbeType::asyncwatchpoint) { LOG(ERROR, ap.loc, err_) << "AOT does not yet support watchpoint probes"; } } Pass CreatePortabilityPass() { auto fn = [](PassContext &ctx) { PortabilityAnalyser analyser(ctx.ast_ctx); if (analyser.analyse()) { // Used by runtime test framework to know when to skip an AOT test if (std::getenv("__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED")) std::cout << "__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED" << std::endl; return PassResult::Error(""); } return PassResult::Success(); }; return Pass("PortabilityAnalyser", fn); } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/portability_analyser.h000066400000000000000000000014741477746507000227000ustar00rootroot00000000000000#pragma once #include #include #include "ast/pass_manager.h" #include "ast/visitor.h" namespace bpftrace { namespace ast { // Checks if a script uses any non-portable bpftrace features that AOT // cannot handle. // // Over time, we expect to relax these restrictions as AOT supports more // features. class PortabilityAnalyser : public Visitor { public: PortabilityAnalyser(ASTContext &ctx, std::ostream &out = std::cerr); int analyse(); using Visitor::visit; void visit(PositionalParameter ¶m); void visit(Builtin &builtin); void visit(Call &call); void visit(Cast &cast); void visit(AttachPoint &ap); private: std::ostream &out_; std::ostringstream err_; }; Pass CreatePortabilityPass(); } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/passes/printer.cpp000066400000000000000000000217171477746507000204600ustar00rootroot00000000000000#include "ast/passes/printer.h" #include #include #include #include #include "ast/ast.h" #include "struct.h" namespace bpftrace::ast { void Printer::print() { ++depth_; visit(ctx_.root); --depth_; } std::string Printer::type(const SizedType &ty) { if (ty.IsNoneTy()) return ""; std::stringstream buf; buf << " :: [" << ty; if (ty.IsCtxAccess()) buf << ", ctx: 1"; if (ty.GetAS() != AddrSpace::none) buf << ", AS(" << ty.GetAS() << ")"; buf << "]"; return buf.str(); } void Printer::visit(Integer &integer) { std::string indent(depth_, ' '); out_ << indent << "int: " << integer.n << type(integer.type) << std::endl; } void Printer::visit(PositionalParameter ¶m) { std::string indent(depth_, ' '); switch (param.ptype) { case PositionalParameterType::positional: out_ << indent << "param: $" << param.n << type(param.type) << std::endl; break; case PositionalParameterType::count: out_ << indent << "param: $#" << type(param.type) << std::endl; break; default: break; } } void Printer::visit(String &string) { std::string indent(depth_, ' '); std::stringstream ss; for (char c : string.str) { // the argument of isprint() must be an unsigned char or EOF int code = static_cast(c); if (std::isprint(code)) { if (c == '\\') ss << "\\\\"; else if (c == '"') ss << "\\\""; else ss << c; } else { if (c == '\n') ss << "\\n"; else if (c == '\t') ss << "\\t"; else if (c == '\r') ss << "\\r"; else ss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << code; } } out_ << indent << "string: " << ss.str() << type(string.type) << std::endl; } void Printer::visit(StackMode &mode) { std::string indent(depth_, ' '); out_ << indent << "stack_mode: " << mode.mode << type(mode.type) << std::endl; } void Printer::visit(Builtin &builtin) { std::string indent(depth_, ' '); out_ << indent << "builtin: " << builtin.ident << type(builtin.type) << std::endl; } void Printer::visit(Identifier &identifier) { std::string indent(depth_, ' '); out_ << indent << "identifier: " << identifier.ident << type(identifier.type) << std::endl; } void Printer::visit(Call &call) { std::string indent(depth_, ' '); out_ << indent << "call: " << call.func << type(call.type) << std::endl; ++depth_; visit(call.vargs); --depth_; } void Printer::visit(Sizeof &szof) { std::string indent(depth_, ' '); out_ << indent << "sizeof: " << type(szof.type) << std::endl; ++depth_; visit(szof.expr); --depth_; } void Printer::visit(Offsetof &offof) { std::string indent(depth_, ' '); out_ << indent << "offsetof: " << type(offof.type) << std::endl; ++depth_; std::string indentParam(depth_, ' '); // Print the args if (offof.expr) { visit(*offof.expr); } else { out_ << indentParam << offof.record << std::endl; } for (const auto &field : offof.field) { out_ << indentParam << field << std::endl; } --depth_; } void Printer::visit(Map &map) { std::string indent(depth_, ' '); out_ << indent << "map: " << map.ident << type(map.type) << std::endl; ++depth_; visit(map.key_expr); --depth_; } void Printer::visit(Variable &var) { std::string indent(depth_, ' '); out_ << indent << "variable: " << var.ident << type(var.type) << std::endl; } void Printer::visit(Binop &binop) { std::string indent(depth_, ' '); out_ << indent << opstr(binop) << type(binop.type) << std::endl; ++depth_; visit(binop.left); visit(binop.right); --depth_; } void Printer::visit(Unop &unop) { std::string indent(depth_, ' '); out_ << indent << opstr(unop) << type(unop.type) << std::endl; ++depth_; visit(unop.expr); --depth_; } void Printer::visit(Ternary &ternary) { std::string indent(depth_, ' '); out_ << indent << "?:" << type(ternary.type) << std::endl; ++depth_; visit(ternary.cond); visit(ternary.left); visit(ternary.right); --depth_; } void Printer::visit(FieldAccess &acc) { std::string indent(depth_, ' '); out_ << indent << "." << type(acc.type) << std::endl; ++depth_; visit(acc.expr); --depth_; if (acc.field.size()) out_ << indent << " " << acc.field << std::endl; else out_ << indent << " " << acc.index << std::endl; } void Printer::visit(ArrayAccess &arr) { std::string indent(depth_, ' '); out_ << indent << "[]" << type(arr.type) << std::endl; ++depth_; visit(arr.expr); visit(arr.indexpr); --depth_; } void Printer::visit(Cast &cast) { std::string indent(depth_, ' '); out_ << indent << "(" << cast.type << ")" << std::endl; ++depth_; visit(cast.expr); --depth_; } void Printer::visit(Tuple &tuple) { std::string indent(depth_, ' '); out_ << indent << "tuple:" << type(tuple.type) << std::endl; ++depth_; visit(tuple.elems); --depth_; } void Printer::visit(ExprStatement &expr) { visit(expr.expr); } void Printer::visit(AssignMapStatement &assignment) { std::string indent(depth_, ' '); out_ << indent << "=" << std::endl; ++depth_; visit(assignment.map); visit(assignment.expr); --depth_; } void Printer::visit(AssignVarStatement &assignment) { std::string indent(depth_, ' '); if (assignment.var_decl_stmt) { visit(assignment.var_decl_stmt); ++depth_; visit(assignment.expr); --depth_; } else { out_ << indent << "=" << std::endl; ++depth_; visit(assignment.var); visit(assignment.expr); --depth_; } } void Printer::visit(AssignConfigVarStatement &assignment) { std::string indent(depth_, ' '); out_ << indent << "=" << std::endl; ++depth_; std::string indentVar(depth_, ' '); out_ << indentVar << "config var: " << assignment.config_var << std::endl; visit(assignment.expr); --depth_; } void Printer::visit(VarDeclStatement &decl) { std::string indent(depth_, ' '); out_ << indent << "decl" << std::endl; ++depth_; visit(decl.var); --depth_; } void Printer::visit(If &if_node) { std::string indent(depth_, ' '); out_ << indent << "if" << std::endl; ++depth_; visit(if_node.cond); ++depth_; out_ << indent << " then" << std::endl; visit(if_node.if_block); if (!if_node.else_block->stmts.empty()) { out_ << indent << " else" << std::endl; visit(if_node.else_block); } depth_ -= 2; } void Printer::visit(Unroll &unroll) { std::string indent(depth_, ' '); out_ << indent << "unroll" << std::endl; ++depth_; visit(unroll.expr); out_ << indent << " block" << std::endl; ++depth_; visit(unroll.block); depth_ -= 2; } void Printer::visit(While &while_block) { std::string indent(depth_, ' '); out_ << indent << "while(" << std::endl; ++depth_; visit(while_block.cond); ++depth_; out_ << indent << " )" << std::endl; visit(while_block.block); } void Printer::visit(For &for_loop) { std::string indent(depth_, ' '); out_ << indent << "for" << std::endl; ++depth_; if (for_loop.ctx_type.IsRecordTy() && !for_loop.ctx_type.GetFields().empty()) { out_ << indent << " ctx\n"; for (const auto &field : for_loop.ctx_type.GetFields()) { out_ << indent << " " << field.name << type(field.type) << "\n"; } } out_ << indent << " decl\n"; ++depth_; visit(for_loop.decl); --depth_; out_ << indent << " expr\n"; ++depth_; visit(for_loop.expr); --depth_; out_ << indent << " stmts\n"; ++depth_; visit(for_loop.stmts); --depth_; --depth_; } void Printer::visit(Config &config) { std::string indent(depth_, ' '); out_ << indent << "config" << std::endl; ++depth_; visit(config.stmts); --depth_; } void Printer::visit(Jump &jump) { std::string indent(depth_, ' '); out_ << indent << opstr(jump) << std::endl; ++depth_; visit(jump.return_value); --depth_; } void Printer::visit(Predicate &pred) { std::string indent(depth_, ' '); out_ << indent << "pred" << std::endl; ++depth_; visit(pred.expr); --depth_; } void Printer::visit(AttachPoint &ap) { std::string indent(depth_, ' '); out_ << indent << ap.name() << std::endl; } void Printer::visit(Probe &probe) { visit(probe.attach_points); ++depth_; visit(probe.pred); visit(probe.block); --depth_; } void Printer::visit(Subprog &subprog) { std::string indent(depth_, ' '); out_ << indent << subprog.name() << ": " << subprog.return_type; out_ << "("; for (size_t i = 0; i < subprog.args.size(); i++) { auto &arg = subprog.args.at(i); out_ << arg->name() << " : " << arg->type; if (i < subprog.args.size() - 1) out_ << ", "; } out_ << ")" << std::endl; ++depth_; visit(subprog.stmts); --depth_; } void Printer::visit(Program &program) { if (program.c_definitions.size() > 0) out_ << program.c_definitions << std::endl; std::string indent(depth_, ' '); out_ << indent << "Program" << std::endl; ++depth_; visit(program.config); --depth_; ++depth_; visit(program.functions); visit(program.probes); --depth_; } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/printer.h000066400000000000000000000030021477746507000201100ustar00rootroot00000000000000#pragma once #include #include "ast/visitor.h" namespace bpftrace { namespace ast { class Printer : public Visitor { public: explicit Printer(ASTContext &ctx, std::ostream &out) : Visitor(ctx), out_(out) { } void print(); using Visitor::visit; void visit(Integer &integer); void visit(PositionalParameter ¶m); void visit(String &string); void visit(StackMode &mode); void visit(Identifier &identifier); void visit(Builtin &builtin); void visit(Call &call); void visit(Sizeof &szof); void visit(Offsetof &offof); void visit(Map &map); void visit(Variable &var); void visit(Binop &binop); void visit(Unop &unop); void visit(Ternary &ternary); void visit(FieldAccess &acc); void visit(ArrayAccess &arr); void visit(Cast &cast); void visit(Tuple &tuple); void visit(ExprStatement &expr); void visit(AssignMapStatement &assignment); void visit(AssignVarStatement &assignment); void visit(AssignConfigVarStatement &assignment); void visit(VarDeclStatement &decl); void visit(If &if_node); void visit(Unroll &unroll); void visit(While &while_block); void visit(For &for_loop); void visit(Config &config); void visit(Jump &jump); void visit(Predicate &pred); void visit(AttachPoint &ap); void visit(Probe &probe); void visit(Subprog &subprog); void visit(Program &program); int depth_ = -1; private: std::ostream &out_; std::string type(const SizedType &ty); }; } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/passes/resource_analyser.cpp000066400000000000000000000437231477746507000225230ustar00rootroot00000000000000#include "resource_analyser.h" #include #include "ast/async_event_types.h" #include "ast/codegen_helper.h" #include "bpftrace.h" #include "globalvars.h" #include "log.h" #include "struct.h" namespace bpftrace::ast { namespace { // This helper differs from SemanticAnalyser::single_provider_type() in that // for situations where a single probetype is required we assume the AST is // well formed. ProbeType single_provider_type_postsema(Probe *probe) { if (!probe->attach_points.empty()) { return probetype(probe->attach_points.at(0)->provider); } return ProbeType::invalid; } std::string get_literal_string(Expression &expr) { String &str = static_cast(expr); return str.str; } } // namespace ResourceAnalyser::ResourceAnalyser(ASTContext &ctx, BPFtrace &bpftrace, std::ostream &out) : Visitor(ctx), bpftrace_(bpftrace), out_(out), probe_(nullptr) { } std::optional ResourceAnalyser::analyse() { visit(ctx_.root); if (!err_.str().empty()) { out_ << err_.str(); return std::nullopt; } if (resources_.max_fmtstring_args_size > 0) { resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::FMT_STRINGS_BUFFER); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::MAX_CPU_ID); } if (resources_.max_tuple_size > 0) { assert(resources_.tuple_buffers > 0); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::TUPLE_BUFFER); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::MAX_CPU_ID); } if (resources_.str_buffers > 0) { resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::GET_STR_BUFFER); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::MAX_CPU_ID); } if (resources_.max_read_map_value_size > 0) { assert(resources_.read_map_value_buffers > 0); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::READ_MAP_VALUE_BUFFER); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::MAX_CPU_ID); } if (resources_.max_write_map_value_size > 0) { resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::WRITE_MAP_VALUE_BUFFER); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::MAX_CPU_ID); } if (resources_.max_variable_size > 0) { assert(resources_.variable_buffers > 0); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::VARIABLE_BUFFER); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::MAX_CPU_ID); } if (resources_.max_map_key_size > 0) { assert(resources_.map_key_buffers > 0); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::MAP_KEY_BUFFER); resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::MAX_CPU_ID); } return std::optional{ std::move(resources_) }; } void ResourceAnalyser::visit(Probe &probe) { probe_ = &probe; Visitor::visit(probe); } void ResourceAnalyser::visit(Subprog &subprog) { probe_ = nullptr; Visitor::visit(subprog); } void ResourceAnalyser::visit(Builtin &builtin) { if (uses_usym_table(builtin.ident)) { // mark probe as using usym, so that the symbol table can be pre-loaded // and symbols resolved even when unavailable at resolution time resources_.probes_using_usym.insert(probe_); } } void ResourceAnalyser::visit(Call &call) { Visitor::visit(call); if (call.func == "printf" || call.func == "system" || call.func == "cat" || call.func == "debugf") { // Implicit first field is the 64bit printf ID. // // Put it in initially so that offset and alignment calculation is // accurate. We'll take it out before saving into resources. std::vector args = { CreateInt64() }; // 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 SizedType ty = (*it)->type; // copy if (!ty.IsAggregate() && !ty.IsTimestampTy()) ty.SetSize(8); args.push_back(ty); } // It may seem odd that we're creating a tuple as part of format // string analysis, but it kinda makes sense. When we transmit // the format string from kernelspace to userspace, we are basically // creating a tuple. Namely: a bunch of values without names, back to // back, and with struct alignment rules. // // Thus, we are good to reuse the padding logic present in tuple // creation to generate offsets for each argument in the args "tuple". auto tuple = Struct::CreateTuple(args); // Remove implicit printf ID field. Downstream consumers do not // expect it nor do they care about it. tuple->fields.erase(tuple->fields.begin()); // Keep track of max "tuple" size needed for fmt string args. Codegen // will use this information to create a percpu array map of large // enough size for all fmt string calls to use. const auto tuple_size = static_cast(tuple->size); if (exceeds_stack_limit(tuple_size)) { resources_.max_fmtstring_args_size = std::max( resources_.max_fmtstring_args_size, static_cast(tuple_size)); } auto fmtstr = get_literal_string(*call.vargs.at(0)); if (call.func == "printf") { if (probe_ != nullptr && single_provider_type_postsema(probe_) == ProbeType::iter) { resources_.bpf_print_fmts.push_back(fmtstr); } else { resources_.printf_args.emplace_back(fmtstr, tuple->fields); } } else if (call.func == "debugf") { resources_.bpf_print_fmts.push_back(fmtstr); } else if (call.func == "system") { resources_.system_args.emplace_back(fmtstr, tuple->fields); } else { resources_.cat_args.emplace_back(fmtstr, tuple->fields); } } else if (call.func == "join") { auto delim = call.vargs.size() > 1 ? get_literal_string(*call.vargs.at(1)) : " "; resources_.join_args.push_back(delim); } else if (call.func == "count" || call.func == "sum" || call.func == "min" || call.func == "max" || call.func == "avg") { resources_.needed_global_vars.insert( bpftrace::globalvars::GlobalVar::NUM_CPUS); } else if (call.func == "hist") { auto &map_info = resources_.maps_info[call.map->ident]; int bits = static_cast(call.vargs.at(1))->n; if (map_info.hist_bits_arg.has_value() && *map_info.hist_bits_arg != bits) { LOG(ERROR, call.loc, err_) << "Different bits in a single hist, had " << *map_info.hist_bits_arg << " now " << bits; } else { map_info.hist_bits_arg = bits; } } else if (call.func == "lhist") { Expression &min_arg = *call.vargs.at(1); Expression &max_arg = *call.vargs.at(2); Expression &step_arg = *call.vargs.at(3); Integer &min = static_cast(min_arg); Integer &max = static_cast(max_arg); Integer &step = static_cast(step_arg); auto args = LinearHistogramArgs{ .min = min.n, .max = max.n, .step = step.n, }; auto &map_info = resources_.maps_info[call.map->ident]; if (map_info.lhist_args.has_value() && *map_info.lhist_args != args) { LOG(ERROR, call.loc, err_) << "Different lhist bounds in a single map unsupported"; } else { map_info.lhist_args = args; } } else if (call.func == "time") { if (call.vargs.size() > 0) resources_.time_args.push_back(get_literal_string(*call.vargs.at(0))); else resources_.time_args.push_back("%H:%M:%S\n"); } else if (call.func == "strftime") { resources_.strftime_args.push_back(get_literal_string(*call.vargs.at(0))); } else if (call.func == "print") { constexpr auto nonmap_headroom = sizeof(AsyncEvent::PrintNonMap); auto &arg = *call.vargs.at(0); if (!arg.is_map) { resources_.non_map_print_args.push_back(arg.type); const size_t fmtstring_args_size = nonmap_headroom + arg.type.GetSize(); if (exceeds_stack_limit(fmtstring_args_size)) { resources_.max_fmtstring_args_size = std::max( resources_.max_fmtstring_args_size, fmtstring_args_size); } } else { auto &map = static_cast(arg); if (map.key_expr) { resources_.non_map_print_args.push_back(map.type); const size_t fmtstring_args_size = nonmap_headroom + map.type.GetSize(); if (exceeds_stack_limit(fmtstring_args_size)) { resources_.max_fmtstring_args_size = std::max( resources_.max_fmtstring_args_size, fmtstring_args_size); } } } } 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("*"); } else if (call.func == "skboutput") { auto &file_arg = *call.vargs.at(0); String &file = static_cast(file_arg); auto &offset_arg = *call.vargs.at(3); Integer &offset = static_cast(offset_arg); resources_.skboutput_args_.emplace_back(file.str, offset.n); resources_.needs_perf_event_map = true; } else if (call.func == "delete") { auto &arg0 = *call.vargs.at(0); auto &map = static_cast(arg0); if (exceeds_stack_limit(map.type.GetSize())) { resources_.max_write_map_value_size = std::max( resources_.max_write_map_value_size, map.type.GetSize()); } } if (call.func == "print" || call.func == "clear" || call.func == "zero") { auto &arg = *call.vargs.at(0); if (arg.is_map) { auto &name = static_cast(arg).ident; auto &map_info = resources_.maps_info[name]; if (map_info.id == -1) map_info.id = next_map_id_++; } } if (call.func == "str" || call.func == "buf" || call.func == "path") { const auto max_strlen = bpftrace_.config_.get(ConfigKeyInt::max_strlen); if (exceeds_stack_limit(max_strlen)) resources_.str_buffers++; } // Aggregation functions like count/sum/max are always called like: // @ = count() // Thus, we visit AssignMapStatement AST node which visits the map and // assigns a map key buffer. Thus, there is no need to assign another // buffer here. // // The exceptions are: // 1. lhist/hist because the map key buffer includes both the key itself // and the bucket ID from a call to linear/log2 functions. // 2. has_key/delete because the map key buffer allocation depends on // arguments to the function e.g. // delete(@, 2) // requires a map key buffer to hold arg1 = 2 but map.key_expr is null // so the map key buffer check in visit(Map &map) doesn't work as is. if (call.func == "lhist" || call.func == "hist") { Map &map = *call.map; // Allocation is always needed for lhist/hist. But we need to allocate // space for both map key and the bucket ID from a call to linear/log2 // functions. const auto map_key_size = map.key_expr ? map.key_type.GetSize() + CreateUInt64().GetSize() : CreateUInt64().GetSize(); if (exceeds_stack_limit(map_key_size)) { resources_.map_key_buffers++; resources_.max_map_key_size = std::max(resources_.max_map_key_size, map_key_size); } } else if (call.func == "has_key") { auto &arg0 = *call.vargs.at(0); auto &map = static_cast(arg0); // has_key does not work on scalar maps (e.g. @a = 1), so we // don't need to check if map.key_expr is set if (needMapKeyAllocation(map, call.vargs.at(1)) && exceeds_stack_limit(map.key_type.GetSize())) { resources_.map_key_buffers++; resources_.max_map_key_size = std::max(resources_.max_map_key_size, map.key_type.GetSize()); } } else if (call.func == "delete") { auto &arg0 = *call.vargs.at(0); auto &map = static_cast(arg0); const auto deleteNeedMapKeyAllocation = call.vargs.size() > 1 ? needMapKeyAllocation(map, call.vargs.at(1)) : needMapKeyAllocation(map); // delete always expects a map and key, so we don't need to check if // map.key_expr is set if (deleteNeedMapKeyAllocation && exceeds_stack_limit(map.key_type.GetSize())) { resources_.map_key_buffers++; resources_.max_map_key_size = std::max(resources_.max_map_key_size, map.key_type.GetSize()); } } if (uses_usym_table(call.func)) { // mark probe as using usym, so that the symbol table can be pre-loaded // and symbols resolved even when unavailable at resolution time resources_.probes_using_usym.insert(probe_); } } void ResourceAnalyser::visit(Map &map) { Visitor::visit(map); update_map_info(map); if (exceeds_stack_limit(map.type.GetSize())) { resources_.read_map_value_buffers++; resources_.max_read_map_value_size = std::max( resources_.max_read_map_value_size, map.type.GetSize()); } maybe_allocate_map_key_buffer(map); } void ResourceAnalyser::visit(Tuple &tuple) { Visitor::visit(tuple); if (exceeds_stack_limit(tuple.type.GetSize())) { resources_.tuple_buffers++; resources_.max_tuple_size = std::max(resources_.max_tuple_size, tuple.type.GetSize()); } } void ResourceAnalyser::visit(For &f) { Visitor::visit(f); // Need tuple per for loop to store key and value if (exceeds_stack_limit(f.decl->type.GetSize())) { resources_.tuple_buffers++; resources_.max_tuple_size = std::max(resources_.max_tuple_size, f.decl->type.GetSize()); } } void ResourceAnalyser::visit(AssignMapStatement &assignment) { // CodegenLLVM traverses the AST like: // AssignmentMapStatement a // | | // visit(a.expr) visit(a.map.key_expr) // // CodegenLLVM avoid traversing into the map node via visit(a.map) // to avoid triggering a map lookup. // // However, ResourceAnalyser traverses the AST differently: // AssignmentMapStatement a // | | // visit(a.expr) visit(a.map) // | // visit(a.map.key_expr) // // Unfortunately, calling ResourceAnalser::visit(a.map) will trigger // an additional read map buffer. Thus to mimic CodegenLLVM, we // skip calling ResourceAnalser::visit(a.map) and do the AST traversal // ourselves. visit(assignment.expr); visit(assignment.map->key_expr); update_map_info(*assignment.map); if (needAssignMapStatementAllocation(assignment)) { if (exceeds_stack_limit(assignment.map->type.GetSize())) { resources_.max_write_map_value_size = std::max( resources_.max_write_map_value_size, assignment.map->type.GetSize()); } } maybe_allocate_map_key_buffer(*assignment.map); } void ResourceAnalyser::visit(Ternary &ternary) { Visitor::visit(ternary); // Codegen cannot use a phi node for ternary string b/c strings can be of // differing lengths and phi node wants identical types. So we have to // allocate a result temporary, but not on the stack b/c a big string would // blow it up. So we need a scratch buffer for it. if (ternary.type.IsStringTy()) { const auto max_strlen = bpftrace_.config_.get(ConfigKeyInt::max_strlen); if (exceeds_stack_limit(max_strlen)) resources_.str_buffers++; } } void ResourceAnalyser::update_variable_info(Variable &var) { // Note we don't check if a variable has been declared/assigned before. // We do this to simplify the code and make it more robust to changes // in other modules at the expense of memory over-allocation. Otherwise, // we would need to track scopes like SemanticAnalyser and CodegenLLVM // and duplicate scope tracking in a third module. if (exceeds_stack_limit(var.type.GetSize())) { resources_.variable_buffers++; resources_.max_variable_size = std::max(resources_.max_variable_size, var.type.GetSize()); } } void ResourceAnalyser::visit(AssignVarStatement &assignment) { Visitor::visit(assignment); update_variable_info(*assignment.var); } void ResourceAnalyser::visit(VarDeclStatement &decl) { Visitor::visit(decl); update_variable_info(*decl.var); } bool ResourceAnalyser::exceeds_stack_limit(size_t size) { return size > bpftrace_.config_.get(ConfigKeyInt::on_stack_limit); } bool ResourceAnalyser::uses_usym_table(const std::string &fun) { return fun == "usym" || fun == "func" || fun == "ustack"; } void ResourceAnalyser::update_map_info(Map &map) { auto &map_info = resources_.maps_info[map.ident]; map_info.value_type = map.type; map_info.key_type = map.key_type; } void ResourceAnalyser::maybe_allocate_map_key_buffer(const Map &map) { const auto map_key_size = map.key_expr ? map.key_type.GetSize() : CreateUInt64().GetSize(); if (needMapKeyAllocation(map) && exceeds_stack_limit(map_key_size)) { resources_.map_key_buffers++; resources_.max_map_key_size = std::max(resources_.max_map_key_size, map_key_size); } } Pass CreateResourcePass() { auto fn = [](PassContext &ctx) { ResourceAnalyser analyser(ctx.ast_ctx, ctx.b); auto pass_result = analyser.analyse(); if (!pass_result.has_value()) return PassResult::Error("Resource", 1); ctx.b.resources = pass_result.value(); return PassResult::Success(); }; return Pass("ResourceAnalyser", fn); } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/resource_analyser.h000066400000000000000000000035751477746507000221710ustar00rootroot00000000000000#pragma once #include #include #include "ast/pass_manager.h" #include "ast/visitor.h" #include "required_resources.h" namespace bpftrace { namespace ast { // Resource analysis pass on AST // // This pass collects information on what runtime resources a script needs. // For example, how many maps to create, what sizes the keys and values are, // all the async printf argument types, etc. // // TODO(danobi): Note that while complete resource collection in this pass is // the goal, there are still places where the goal is not yet realized. For // example the helper error metadata is still being collected during codegen. class ResourceAnalyser : public Visitor { public: ResourceAnalyser(ASTContext &ctx, BPFtrace &bpftrace, std::ostream &out = std::cerr); std::optional analyse(); using Visitor::visit; void visit(Probe &probe); void visit(Subprog &subprog); void visit(Builtin &map); void visit(Call &call); void visit(Map &map); void visit(Tuple &tuple); void visit(For &f); void visit(Ternary &ternary); void visit(AssignMapStatement &assignment); void visit(AssignVarStatement &assignment); void visit(VarDeclStatement &decl); private: // Determines whether the given function uses userspace symbol resolution. // This is used later for loading the symbol table into memory. bool uses_usym_table(const std::string &fun); bool exceeds_stack_limit(size_t size); void maybe_allocate_map_key_buffer(const Map &map); void update_map_info(Map &map); void update_variable_info(Variable &var); RequiredResources resources_; BPFtrace &bpftrace_; std::ostream &out_; std::ostringstream err_; // Current probe we're analysing Probe *probe_; int next_map_id_ = 0; }; Pass CreateResourcePass(); } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/passes/return_path_analyser.cpp000066400000000000000000000031771477746507000232260ustar00rootroot00000000000000#include "return_path_analyser.h" #include "log.h" namespace bpftrace::ast { ReturnPathAnalyser::ReturnPathAnalyser(ASTContext &ctx, std::ostream &out) : Visitor(ctx), out_(out) { } bool ReturnPathAnalyser::visit(Program &prog) { for (Subprog *subprog : prog.functions) { if (!visit(*subprog)) return false; } return true; } bool ReturnPathAnalyser::visit(Subprog &subprog) { if (subprog.return_type.IsVoidTy()) return true; for (Statement *stmt : subprog.stmts) { if (visit(*stmt)) return true; } LOG(ERROR, subprog.loc, err_) << "Not all code paths returned a value"; return false; } bool ReturnPathAnalyser::visit(Jump &jump) { return jump.ident == JumpType::RETURN; } bool ReturnPathAnalyser::visit(If &if_node) { bool result = false; for (Statement *stmt : if_node.if_block->stmts) { if (visit(stmt)) result = true; } if (!result) { // if block has no return return false; } for (Statement *stmt : if_node.else_block->stmts) { if (visit(stmt)) { // both blocks have a return return true; } } // else block has no return (or there is no else block) return false; } int ReturnPathAnalyser::analyse() { int result = visit(ctx_.root) ? 0 : 1; if (result) out_ << err_.str(); return result; } Pass CreateReturnPathPass() { auto fn = [](PassContext &ctx) { auto return_path = ReturnPathAnalyser(ctx.ast_ctx); int err = return_path.analyse(); if (err) return PassResult::Error("ReturnPath"); return PassResult::Success(); }; return Pass("ReturnPath", fn); } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/return_path_analyser.h000066400000000000000000000014051477746507000226630ustar00rootroot00000000000000#pragma once #include "ast/pass_manager.h" #include "ast/visitor.h" namespace bpftrace { namespace ast { class ReturnPathAnalyser : public Visitor { public: explicit ReturnPathAnalyser(ASTContext &ctx, std::ostream &out = std::cerr); // visit methods return true iff all return paths of the analyzed code // (represented by the given node) return a value // For details for concrete node type see the implementations using Visitor::visit; bool visit(Program &prog); bool visit(Subprog &subprog); bool visit(Jump &jump); bool visit(If &if_stmt); int analyse(); private: std::ostream &out_; std::ostringstream err_; }; Pass CreateReturnPathPass(); } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/passes/semantic_analyser.cpp000066400000000000000000004320241477746507000224730ustar00rootroot00000000000000#include "semantic_analyser.h" #include #include #include #include #include #include #include "arch/arch.h" #include "ast/ast.h" #include "ast/async_event_types.h" #include "ast/signal_bt.h" #include "collect_nodes.h" #include "config.h" #include "log.h" #include "printf.h" #include "probe_matcher.h" #include "tracepoint_format_parser.h" #include "types.h" #include "usdt.h" namespace bpftrace::ast { static constexpr std::string_view DELETE_ERROR = "delete() expects a map for the first argument and a key for the second " "argument e.g. `delete(@my_map, 1);`"; static const std::map> &getIntcasts() { static const std::map> intcasts = { { "uint8", std::tuple{ 8, false } }, { "int8", std::tuple{ 8, true } }, { "uint16", std::tuple{ 16, false } }, { "int16", std::tuple{ 16, true } }, { "uint32", std::tuple{ 32, false } }, { "int32", std::tuple{ 32, true } }, { "uint64", std::tuple{ 64, false } }, { "int64", std::tuple{ 64, true } }, }; return intcasts; } static std::pair getUIntTypeRange(const SizedType &ty) { assert(ty.IsIntegerTy()); auto size = ty.GetSize(); switch (size) { case 1: return { 0, std::numeric_limits::max() }; case 2: return { 0, std::numeric_limits::max() }; case 4: return { 0, std::numeric_limits::max() }; case 8: return { 0, std::numeric_limits::max() }; default: LOG(BUG) << "Unrecognized int type size: " << size; return { 0, 0 }; } } static std::pair getIntTypeRange(const SizedType &ty) { assert(ty.IsIntegerTy()); auto size = ty.GetSize(); switch (size) { case 1: return { std::numeric_limits::min(), std::numeric_limits::max() }; case 2: return { std::numeric_limits::min(), std::numeric_limits::max() }; case 4: return { std::numeric_limits::min(), std::numeric_limits::max() }; case 8: return { std::numeric_limits::min(), std::numeric_limits::max() }; default: LOG(BUG) << "Unrecognized int type size: " << size; return { 0, 0 }; } } // These are types which aren't valid for scratch variables // e.g. this is not valid `let $x: sum_t;` static bool IsValidVarDeclType(const SizedType &ty) { switch (ty.GetTy()) { case Type::avg_t: case Type::count_t: case Type::hist_t: case Type::lhist_t: case Type::max_t: case Type::min_t: case Type::stats_t: case Type::sum_t: case Type::stack_mode: case Type::voidtype: return false; case Type::integer: case Type::kstack_t: case Type::ustack_t: case Type::timestamp: case Type::ksym_t: case Type::usym_t: case Type::inet: case Type::username: case Type::string: case Type::buffer: case Type::pointer: case Type::array: case Type::mac_address: case Type::record: case Type::tuple: case Type::cgroup_path_t: case Type::strerror_t: case Type::none: case Type::reference: case Type::timestamp_mode: return true; } return false; // unreachable } // These are special map aggregation types that cannot be assigned // to scratch variables or from one map to another e.g. these are both invalid: // `@a = hist(10); let $b = @a;` // `@a = count(); @b = @a;` // However, if the assigned map already contains integers, we implicitly cast // the aggregation into an integer to retrieve its value, so this is valid: // `@a = count(); @b = 0; @b = @a` bool SemanticAnalyser::is_valid_assignment(const Expression *target, const Expression *expr) { if (expr->is_map) { // Prevent assigning multi-output aggregations to another map. if (expr->type.IsMultiOutputMapTy()) return false; // Prevent declaring a map copying another aggregate map. if (auto *target_map = dynamic_cast(target)) { bool map_has_type = get_map_type(*target_map); if (expr->type.IsCastableMapTy() && map_has_type == false) return false; } } return true; } void SemanticAnalyser::visit(Integer &integer) { if (integer.is_negative) { integer.type = CreateInt64(); } else { // Default to signed unless the original value is too large uint64_t val = static_cast(integer.n); if (val > std::numeric_limits::max()) { integer.type = CreateUInt64(); } else { integer.type = CreateInt64(); } } } void SemanticAnalyser::visit(PositionalParameter ¶m) { param.type = CreateInt64(); if (func_ == "str") { param.is_in_str = true; has_pos_param_ = true; } switch (param.ptype) { case PositionalParameterType::positional: if (param.n <= 0) LOG(ERROR, param.loc, err_) << "$" << std::to_string(param.n) + " is not a valid parameter"; if (is_final_pass()) { std::string pstr = bpftrace_.get_param(param.n, param.is_in_str); auto param_int = get_int_from_str(pstr); if (!param_int.has_value() && !param.is_in_str) { LOG(ERROR, param.loc, err_) << "$" << param.n << " used numerically but given \"" << pstr << "\". Try using str($" << param.n << ")."; } if (param_int && std::holds_alternative(*param_int)) { param.type = CreateUInt64(); } // string allocated in bpf stack. See codegen. if (param.is_in_str) param.type.SetAS(AddrSpace::kernel); } break; case PositionalParameterType::count: if (param.is_in_str) { LOG(ERROR, param.loc, err_) << "use $#, not str($#)"; } break; default: LOG(ERROR, param.loc, err_) << "unknown parameter type"; param.type = CreateNone(); break; } } 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) return; auto str_len = bpftrace_.config_.get(ConfigKeyInt::max_strlen); if (!is_compile_time_func(func_) && string.str.size() > str_len - 1) { LOG(ERROR, string.loc, err_) << "String is too long (over " << str_len << " bytes): " << string.str; } // @a = buf("hi", 2). String allocated on bpf stack. See codegen string.type.SetAS(AddrSpace::kernel); } void SemanticAnalyser::visit(StackMode &mode) { auto stack_mode = bpftrace::Config::get_stack_mode(mode.mode); if (stack_mode.has_value()) { mode.type = CreateStackMode(); mode.type.stack_type.mode = stack_mode.value(); } else { mode.type = CreateNone(); LOG(ERROR, mode.loc, err_) << "Unknown stack mode: '" + mode.mode + "'"; } } void SemanticAnalyser::visit(Identifier &identifier) { if (bpftrace_.enums_.count(identifier.ident) != 0) { const auto &enum_name = std::get<1>(bpftrace_.enums_[identifier.ident]); identifier.type = CreateEnum(64, enum_name); } else if (bpftrace_.structs.Has(identifier.ident)) { identifier.type = CreateRecord(identifier.ident, bpftrace_.structs.Lookup(identifier.ident)); } else if (func_ == "sizeof" && getIntcasts().count(identifier.ident) != 0) { identifier.type = CreateInt( std::get<0>(getIntcasts().at(identifier.ident))); } else if (func_ == "nsecs") { identifier.type = CreateTimestampMode(); if (identifier.ident == "monotonic") { identifier.type.ts_mode = TimestampMode::monotonic; } else if (identifier.ident == "boot") { identifier.type.ts_mode = TimestampMode::boot; } else if (identifier.ident == "tai") { identifier.type.ts_mode = TimestampMode::tai; } else if (identifier.ident == "sw_tai") { identifier.type.ts_mode = TimestampMode::sw_tai; } else { LOG(ERROR, identifier.loc, err_) << "Invalid timestamp mode: " << identifier.ident; } } else { identifier.type = CreateNone(); LOG(ERROR, identifier.loc, err_) << "Unknown identifier: '" + identifier.ident + "'"; } } void SemanticAnalyser::builtin_args_tracepoint(AttachPoint *attach_point, Builtin &builtin) { // tracepoint wildcard expansion, part 2 of 3. This: // 1. expands the wildcard, then sets args to be the first matched probe. // This is so that enough of the type information is available to // survive the later semantic analyser checks. // 2. sets is_tparg so that codegen does the real type setting after // expansion. auto matches = bpftrace_.probe_matcher_->get_matches_for_ap(*attach_point); if (!matches.empty()) { auto &match = *matches.begin(); std::string tracepoint_struct = TracepointFormatParser::get_struct_name( match); builtin.type = CreateRecord(tracepoint_struct, bpftrace_.structs.Lookup(tracepoint_struct)); builtin.type.SetAS(attach_point->target == "syscalls" ? AddrSpace::user : AddrSpace::kernel); builtin.type.MarkCtxAccess(); builtin.type.is_tparg = true; } } ProbeType SemanticAnalyser::single_provider_type(Probe *probe) { ProbeType type = ProbeType::invalid; for (auto *attach_point : probe->attach_points) { ProbeType ap = probetype(attach_point->provider); if (type == ProbeType::invalid) type = ap; if (type != ap) return ProbeType::invalid; } return type; } AddrSpace SemanticAnalyser::find_addrspace(ProbeType pt) { switch (pt) { case ProbeType::kprobe: case ProbeType::kretprobe: case ProbeType::fentry: case ProbeType::fexit: case ProbeType::tracepoint: case ProbeType::iter: case ProbeType::rawtracepoint: return AddrSpace::kernel; case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: return AddrSpace::user; // case : i:ms:1 (struct x*)ctx)->x // Cannot decide the addrspace. Provide backward compatibility, // if addrspace cannot be detected. case ProbeType::invalid: case ProbeType::special: case ProbeType::profile: case ProbeType::interval: case ProbeType::software: case ProbeType::hardware: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: // Will trigger a warning in selectProbeReadHelper. return AddrSpace::none; } return {}; // unreached } void SemanticAnalyser::visit(Builtin &builtin) { if (builtin.ident == "ctx") { auto probe = get_probe(builtin.loc, builtin.ident); if (probe == nullptr) return; ProbeType pt = probetype(probe->attach_points[0]->provider); libbpf::bpf_prog_type bt = progtype(pt); std::string func = probe->attach_points[0]->func; for (auto *attach_point : probe->attach_points) { ProbeType pt = probetype(attach_point->provider); libbpf::bpf_prog_type bt2 = progtype(pt); if (bt != bt2) LOG(ERROR, builtin.loc, err_) << "ctx cannot be used in different BPF program types: " << progtypeName(bt) << " and " << progtypeName(bt2); } switch (static_cast(bt)) { case libbpf::BPF_PROG_TYPE_KPROBE: builtin.type = CreatePointer(CreateRecord("struct pt_regs", bpftrace_.structs.Lookup( "struct pt_regs")), AddrSpace::kernel); builtin.type.MarkCtxAccess(); break; case libbpf::BPF_PROG_TYPE_TRACEPOINT: LOG(ERROR, builtin.loc, err_) << "Use args instead of ctx in tracepoint"; break; case libbpf::BPF_PROG_TYPE_PERF_EVENT: builtin.type = CreatePointer( CreateRecord("struct bpf_perf_event_data", bpftrace_.structs.Lookup( "struct bpf_perf_event_data")), AddrSpace::kernel); builtin.type.MarkCtxAccess(); break; case libbpf::BPF_PROG_TYPE_TRACING: if (pt == ProbeType::iter) { std::string type = "struct bpf_iter__" + func; builtin.type = CreatePointer( CreateRecord(type, bpftrace_.structs.Lookup(type)), AddrSpace::kernel); builtin.type.MarkCtxAccess(); builtin.type.is_btftype = true; } else { LOG(ERROR, builtin.loc, err_) << "invalid program type"; } break; default: LOG(ERROR, builtin.loc, err_) << "invalid program type"; break; } } else if (builtin.ident == "pid" || builtin.ident == "tid") { builtin.type = CreateUInt32(); } else if (builtin.ident == "nsecs" || builtin.ident == "elapsed" || builtin.ident == "cgroup" || builtin.ident == "uid" || builtin.ident == "gid" || builtin.ident == "cpu" || builtin.ident == "rand" || builtin.ident == "numaid" || builtin.ident == "jiffies") { builtin.type = CreateUInt64(); if (builtin.ident == "cgroup" && !bpftrace_.feature_->has_helper_get_current_cgroup_id()) { LOG(ERROR, builtin.loc, err_) << "BPF_FUNC_get_current_cgroup_id is not available for your kernel " "version"; } else if (builtin.ident == "jiffies" && !bpftrace_.feature_->has_helper_jiffies64()) { LOG(ERROR, builtin.loc, err_) << "BPF_FUNC_jiffies64 is not available for your kernel version"; } } else if (builtin.ident == "curtask") { // Retype curtask to its original type: struct task_struct. builtin.type = CreatePointer(CreateRecord("struct task_struct", bpftrace_.structs.Lookup( "struct task_struct")), AddrSpace::kernel); } else if (builtin.ident == "retval") { auto probe = get_probe(builtin.loc, builtin.ident); if (probe == nullptr) return; ProbeType type = single_provider_type(probe); if (type == ProbeType::kretprobe || type == ProbeType::uretprobe) { builtin.type = CreateUInt64(); } else if (type == ProbeType::fentry || type == ProbeType::fexit) { auto arg = bpftrace_.structs.GetProbeArg(*probe, RETVAL_FIELD_NAME); if (arg) { builtin.type = arg->type; builtin.type.is_btftype = true; } else LOG(ERROR, builtin.loc, err_) << "Can't find a field " << RETVAL_FIELD_NAME; } else { LOG(ERROR, builtin.loc, err_) << "The retval builtin can only be used with 'kretprobe' and " << "'uretprobe' and 'fentry' probes" << (type == ProbeType::tracepoint ? " (try to use args.ret instead)" : ""); } // For kretprobe, fentry, fexit -> AddrSpace::kernel // For uretprobe -> AddrSpace::user builtin.type.SetAS(find_addrspace(type)); } else if (builtin.ident == "kstack") { builtin.type = CreateStack(true, StackType{ .mode = bpftrace_.config_.get( ConfigKeyStackMode::default_) }); } else if (builtin.ident == "ustack") { builtin.type = CreateStack(false, StackType{ .mode = bpftrace_.config_.get( ConfigKeyStackMode::default_) }); } else if (builtin.ident == "comm") { builtin.type = CreateString(COMM_SIZE); // comm allocated in the bpf stack. See codegen // Case: @=comm and strncmp(@, "name") builtin.type.SetAS(AddrSpace::kernel); } else if (builtin.ident == "func") { auto probe = get_probe(builtin.loc, builtin.ident); if (probe == nullptr) return; for (auto *attach_point : probe->attach_points) { ProbeType type = probetype(attach_point->provider); if (type == ProbeType::kprobe || type == ProbeType::kretprobe) builtin.type = CreateKSym(); else if (type == ProbeType::uprobe || type == ProbeType::uretprobe) builtin.type = CreateUSym(); else if (type == ProbeType::fentry || type == ProbeType::fexit) { if (!bpftrace_.feature_->has_helper_get_func_ip()) { LOG(ERROR, builtin.loc, err_) << "BPF_FUNC_get_func_ip not available for your kernel version"; } builtin.type = CreateKSym(); } else LOG(ERROR, builtin.loc, err_) << "The func builtin can not be used with '" << attach_point->provider << "' probes"; if ((type == ProbeType::kretprobe || type == ProbeType::uretprobe) && !bpftrace_.feature_->has_helper_get_func_ip()) { LOG(ERROR, builtin.loc, err_) << "The 'func' builtin is not available for " << type << "s on kernels without the get_func_ip BPF feature. Consider " "using the 'probe' builtin instead."; } } } else if (builtin.is_argx()) { auto probe = get_probe(builtin.loc, builtin.ident); if (probe == nullptr) return; ProbeType pt = probetype(probe->attach_points[0]->provider); AddrSpace addrspace = find_addrspace(pt); for (auto *attach_point : probe->attach_points) { ProbeType type = probetype(attach_point->provider); if (type == ProbeType::uprobe && bpftrace_.config_.get(ConfigKeyBool::probe_inline)) LOG(ERROR, builtin.loc, err_) << "The " + builtin.ident + " builtin can only be used when " << "the probe_inline config is disabled."; if (type != ProbeType::kprobe && type != ProbeType::uprobe && type != ProbeType::usdt && type != ProbeType::rawtracepoint) LOG(ERROR, builtin.loc, err_) << "The " << builtin.ident << " builtin can only be used with " << "'kprobes', 'uprobes' and 'usdt' probes"; } int arg_num = atoi(builtin.ident.substr(3).c_str()); if (arg_num > arch::max_arg()) LOG(ERROR, builtin.loc, err_) << arch::name() << " doesn't support " << builtin.ident; builtin.type = CreateUInt64(); builtin.type.SetAS(addrspace); } else if (!builtin.ident.compare(0, 4, "sarg") && builtin.ident.size() == 5 && builtin.ident.at(4) >= '0' && builtin.ident.at(4) <= '9') { auto probe = get_probe(builtin.loc, builtin.ident); if (probe == nullptr) return; ProbeType pt = probetype(probe->attach_points[0]->provider); AddrSpace addrspace = find_addrspace(pt); for (auto *attach_point : probe->attach_points) { ProbeType type = probetype(attach_point->provider); if (type != ProbeType::kprobe && type != ProbeType::uprobe) LOG(ERROR, builtin.loc, err_) << "The " + builtin.ident << " builtin can only be used with 'kprobes' and 'uprobes' probes"; if (type == ProbeType::uprobe && bpftrace_.config_.get(ConfigKeyBool::probe_inline)) LOG(ERROR, builtin.loc, err_) << "The " + builtin.ident + " builtin can only be used when " << "the probe_inline config is disabled."; if (is_final_pass() && (attach_point->address != 0 || attach_point->func_offset != 0)) { // If sargX values are needed when using an offset, they can be stored // in a map when entering the function and then referenced from an // offset-based probe LOG(WARNING, builtin.loc, out_) << "Using an address offset with the sargX built-in can" "lead to unexpected behavior "; } } builtin.type = CreateUInt64(); builtin.type.SetAS(addrspace); } else if (builtin.ident == "probe") { auto probe = get_probe(builtin.loc, builtin.ident); if (probe == nullptr) return; size_t str_size = 0; for (AttachPoint *attach_point : probe->attach_points) { auto matches = bpftrace_.probe_matcher_->get_matches_for_ap( *attach_point); for (const auto &match : matches) { // No need to preserve this node, as we are just expanding to see the // size of the name. This could be refactored into a separate pass. ASTContext dummyctx; str_size = std::max(str_size, attach_point->create_expansion_copy(dummyctx, match) .name() .length()); } } builtin.type = CreateString(str_size + 1); probe->need_expansion = true; } else if (builtin.ident == "username") { builtin.type = CreateUsername(); } else if (builtin.ident == "cpid") { if (!has_child_) { LOG(ERROR, builtin.loc, err_) << "cpid cannot be used without child command"; } builtin.type = CreateUInt32(); } else if (builtin.ident == "args") { auto probe = get_probe(builtin.loc, builtin.ident); if (probe == nullptr) return; for (auto *attach_point : probe->attach_points) { ProbeType type = probetype(attach_point->provider); if (type == ProbeType::tracepoint) { attach_point->expansion = ExpansionType::FULL; builtin_args_tracepoint(attach_point, builtin); } } ProbeType type = single_provider_type(probe); if (type == ProbeType::invalid) { LOG(ERROR, builtin.loc, err_) << "The args builtin can only be used within the context of a single " "probe type, e.g. \"probe1 {args}\" is valid while " "\"probe1,probe2 {args}\" is not."; } else if (type == ProbeType::fentry || type == ProbeType::fexit || type == ProbeType::uprobe) { if (type == ProbeType::uprobe && bpftrace_.config_.get(ConfigKeyBool::probe_inline)) LOG(ERROR, builtin.loc, err_) << "The args builtin can only be used when " << "the probe_inline config is disabled."; auto type_name = probe->args_typename(); builtin.type = CreateRecord(type_name, bpftrace_.structs.Lookup(type_name)); if (builtin.type.GetFieldCount() == 0) LOG(ERROR, builtin.loc, err_) << "Cannot read function parameters"; builtin.type.MarkCtxAccess(); builtin.type.is_funcarg = true; builtin.type.SetAS(type == ProbeType::uprobe ? AddrSpace::user : AddrSpace::kernel); // We'll build uprobe args struct on stack if (type == ProbeType::uprobe) builtin.type.is_internal = true; } else if (type != ProbeType::tracepoint) // no special action for // tracepoint { LOG(ERROR, builtin.loc, err_) << "The args builtin can only be used with " "tracepoint/fentry/uprobe probes (" << type << " used here)"; } } else { builtin.type = CreateNone(); LOG(ERROR, builtin.loc, err_) << "Unknown builtin variable: '" << builtin.ident << "'"; } } namespace { bool skip_key_validation(const Call &call) { return call.func == "print" || call.func == "clear" || call.func == "zero" || call.func == "len"; } } // namespace void SemanticAnalyser::visit(Call &call) { // Check for unsafe-ness first. It is likely the most pertinent issue // (and should be at the top) for any function call. if (bpftrace_.safe_mode_ && is_unsafe_func(call.func)) { LOG(ERROR, call.loc, err_) << call.func << "() is an unsafe function being used in safe mode"; } struct func_setter { func_setter(SemanticAnalyser &analyser, const std::string &s) : analyser_(analyser), old_func_(analyser_.func_) { analyser_.func_ = s; } ~func_setter() { analyser_.func_ = old_func_; analyser_.func_arg_idx_ = -1; } private: SemanticAnalyser &analyser_; std::string old_func_; }; func_setter scope_bound_func_setter{ *this, call.func }; for (size_t i = 0; i < call.vargs.size(); ++i) { auto &expr = *call.vargs[i]; func_arg_idx_ = i; if (expr.is_map) { Map &map = static_cast(expr); // If the map is indexed, don't skip key validation if (map.key_expr == nullptr) { // These calls expect just a map reference for the first argument if ((call.func == "delete" || call.func == "has_key") && i == 0) { map.skip_key_validation = true; map.is_read = false; } else if (skip_key_validation(call)) { map.skip_key_validation = true; map.is_read = false; } } } call.vargs[i] = dereference_if_needed(call.vargs[i]); } if (auto probe = dynamic_cast(top_level_node_)) { for (auto *ap : probe->attach_points) { if (!check_available(call, *ap)) { LOG(ERROR, call.loc, err_) << call.func << " can not be used with \"" << ap->provider << "\" probes"; } } } if (call.func == "hist") { check_assignment(call, true, false, false); if (!check_varargs(call, 1, 2)) return; if (call.vargs.size() == 1) { call.vargs.push_back(ctx_.make_node(0, call.loc)); // default // bits is 0 } else { if (!check_arg(call, Type::integer, 1, true)) return; const auto bits = bpftrace_.get_int_literal(call.vargs.at(1)); if (!bits.has_value()) { // Bug here as the validity of the integer literal is already checked by // check_arg above. LOG(BUG) << call.func << ": invalid bits value"; } else if (*bits < 0 || *bits > 5) { LOG(ERROR, call.loc, err_) << call.func << ": bits " << *bits << " must be 0..5"; } } check_arg(call, Type::integer, 0); call.type = CreateHist(); } else if (call.func == "lhist") { check_assignment(call, true, false, false); if (check_nargs(call, 4)) { check_arg(call, Type::integer, 0, false); check_arg(call, Type::integer, 1, true); check_arg(call, Type::integer, 2, true); check_arg(call, Type::integer, 3, true); } if (is_final_pass()) { Expression *min_arg = call.vargs.at(1); Expression *max_arg = call.vargs.at(2); Expression *step_arg = call.vargs.at(3); auto min = bpftrace_.get_int_literal(min_arg); auto max = bpftrace_.get_int_literal(max_arg); auto step = bpftrace_.get_int_literal(step_arg); if (!min.has_value()) { LOG(ERROR, call.loc, err_) << call.func << ": invalid min value"; return; } if (!max.has_value()) { LOG(ERROR, call.loc, err_) << call.func << ": invalid max value"; return; } if (!step.has_value()) { LOG(ERROR, call.loc, err_) << call.func << ": invalid step value"; return; } if (*step <= 0) { LOG(ERROR, call.loc, err_) << "lhist() step must be >= 1 (" << *step << " provided)"; } else { int buckets = (*max - *min) / *step; if (buckets > 1000) { LOG(ERROR, call.loc, err_) << "lhist() too many buckets, must be <= 1000 (would need " << buckets << ")"; } } if (*min < 0) { LOG(ERROR, call.loc, err_) << "lhist() min must be non-negative (provided min " << *min << ")"; } if (*min > *max) { LOG(ERROR, call.loc, err_) << "lhist() min must be less than max (provided min " << *min << " and max " << *max << ")"; } if ((*max - *min) < *step) { LOG(ERROR, call.loc, err_) << "lhist() step is too large for the given range (provided step " << *step << " for range " << (*max - *min) << ")"; } } call.type = CreateLhist(); } else if (call.func == "count") { check_assignment(call, true, false, false); (void)check_nargs(call, 0); call.type = CreateCount(true); } else if (call.func == "sum") { bool sign = false; check_assignment(call, true, false, false); if (check_nargs(call, 1)) { check_arg(call, Type::integer, 0); sign = call.vargs.at(0)->type.IsSigned(); } call.type = CreateSum(sign); } else if (call.func == "min") { bool sign = false; check_assignment(call, true, false, false); if (check_nargs(call, 1)) { check_arg(call, Type::integer, 0); sign = call.vargs.at(0)->type.IsSigned(); } call.type = CreateMin(sign); } else if (call.func == "max") { bool sign = false; check_assignment(call, true, false, false); if (check_nargs(call, 1)) { check_arg(call, Type::integer, 0); sign = call.vargs.at(0)->type.IsSigned(); } call.type = CreateMax(sign); } else if (call.func == "avg") { check_assignment(call, true, false, false); if (check_nargs(call, 1)) { check_arg(call, Type::integer, 0); } call.type = CreateAvg(true); } else if (call.func == "stats") { check_assignment(call, true, false, false); if (check_nargs(call, 1)) { check_arg(call, Type::integer, 0); } call.type = CreateStats(true); } else if (call.func == "delete") { check_assignment(call, false, false, false); if (check_varargs(call, 1, 2)) { if (!call.vargs.at(0)->is_map) { LOG(ERROR, call.vargs.at(0)->loc, err_) << DELETE_ERROR; } else { Map &map = static_cast(*call.vargs.at(0)); if (call.vargs.size() == 1) { if (map.key_expr) { // We're modifying the AST here to support the deprecated delete // API. Once we remove the old API, we can delete this. call.vargs.push_back(map.key_expr); map.key_expr = nullptr; } else if (is_final_pass()) { auto *map_key_type = get_map_key_type(map); if (map_key_type && !map_key_type->IsNoneTy()) { LOG(ERROR, call.vargs.at(0)->loc, err_) << DELETE_ERROR; } } } else { if (map.key_expr) { LOG(ERROR, call.vargs.at(0)->loc, err_) << "delete() expects a map with no keys for the first argument"; } auto *map_key_type = get_map_key_type(map); if (map_key_type) { auto &arg1 = *call.vargs.at(1); SizedType new_key_type = create_key_type(arg1.type, arg1.loc); update_current_key(*map_key_type, new_key_type); validate_new_key(*map_key_type, new_key_type, map.ident, arg1.loc); } } } } call.type = CreateNone(); } else if (call.func == "has_key") { if (check_varargs(call, 2, 2)) { auto &arg0 = *call.vargs.at(0); if (!arg0.is_map) { LOG(ERROR, arg0.loc, err_) << "has_key() expects the first argument to be a map"; } else { Map &map = static_cast(arg0); if (map.key_expr) { LOG(ERROR, arg0.loc, err_) << "has_key() expects the first argument to be a map. Not a map " "value expression."; } auto *mapkey = get_map_key_type(map); if (mapkey) { if (mapkey->IsNoneTy()) { LOG(ERROR, arg0.loc, err_) << "has_key() only accepts maps that have keys. No scalar maps " "e.g. `@a = 1;`"; } else { auto &arg1 = *call.vargs.at(1); SizedType new_key_type = create_key_type(arg1.type, arg1.loc); update_current_key(*mapkey, new_key_type); validate_new_key(*mapkey, new_key_type, map.ident, arg1.loc); } } // Note: if the map key is null after the final pass we'll // get an error in the Map visitor about the whole map being undefined // so no need to add a second, similar error here. } } // TODO: this should be a bool type but that type is currently broken // as a value for variables and maps // https://github.com/bpftrace/bpftrace/issues/3502 call.type = CreateUInt8(); } else if (call.func == "str") { if (check_varargs(call, 1, 2)) { auto *arg = call.vargs.at(0); auto &t = arg->type; if (!t.IsIntegerTy() && !t.IsPtrTy()) { LOG(ERROR, call.loc, err_) << call.func << "() expects an integer or a pointer type as first " << "argument (" << t << " provided)"; } auto strlen = bpftrace_.config_.get(ConfigKeyInt::max_strlen); if (call.vargs.size() == 2 && check_arg(call, Type::integer, 1, false)) { auto &size_arg = *call.vargs.at(1); if (size_arg.is_literal) { auto &integer = static_cast(size_arg); long value = integer.n; if (value < 0) { if (is_final_pass()) LOG(ERROR, call.loc, err_) << call.func << "cannot use negative length (" << value << ")"; } else if (value > static_cast(strlen)) { if (is_final_pass()) LOG(WARNING, call.loc, out_) << "length param (" << value << ") is too long and will be shortened to " << strlen << " bytes (see BPFTRACE_MAX_STRLEN)"; } else { strlen = value; } } } call.type = CreateString(strlen); if (has_pos_param_) { if (dynamic_cast(arg)) call.is_literal = true; else { auto binop = dynamic_cast(arg); if (!(binop && (dynamic_cast(binop->left) || dynamic_cast(binop->right)))) { // Only str($1), str($1 + CONST), or str(CONST + $1) are allowed LOG(ERROR, call.loc, err_) << call.func << "() only accepts positional parameters" << " directly or with a single constant offset added"; } } } // Required for cases like strncmp(str($1), str(2), 4)) call.type.SetAS(AddrSpace::kernel); } has_pos_param_ = false; } else if (call.func == "buf") { const uint64_t max_strlen = bpftrace_.config_.get(ConfigKeyInt::max_strlen); if (max_strlen > std::numeric_limits::max()) { LOG(ERROR, call.loc, err_) << "BPFTRACE_MAX_STRLEN too large to use on buffer (" << max_strlen << " > " << std::numeric_limits::max() << ")"; } if (!check_varargs(call, 1, 2)) return; auto &arg = *call.vargs.at(0); if (is_final_pass() && !(arg.type.IsIntTy() || arg.type.IsStringTy() || arg.type.IsPtrTy() || arg.type.IsArrayTy())) { LOG(ERROR, call.loc, err_) << call.func << "() expects an integer, string, or array argument but saw " << typestr(arg.type.GetTy()); } // Subtract out metadata headroom uint32_t max_buffer_size = max_strlen - sizeof(AsyncEvent::Buf); uint32_t buffer_size = max_buffer_size; if (call.vargs.size() == 1) { if (arg.type.IsArrayTy()) buffer_size = arg.type.GetNumElements() * arg.type.GetElementTy()->GetSize(); else if (is_final_pass()) LOG(ERROR, call.loc, err_) << call.func << "() expects a length argument for non-array type " << typestr(arg.type.GetTy()); } else { if (is_final_pass()) check_arg(call, Type::integer, 1, false); auto &size_arg = *call.vargs.at(1); if (size_arg.type.IsIntTy() && size_arg.is_literal) { auto value = bpftrace_.get_int_literal(&size_arg); if (value.has_value()) { if (*value < 0) { LOG(ERROR, call.loc, err_) << call.func << " cannot use negative length (" << *value << ")"; } buffer_size = *value; } else LOG(ERROR, call.loc, err_) << call.func << ": invalid length value"; } } if (buffer_size > max_buffer_size) { if (is_final_pass()) LOG(WARNING, call.loc, out_) << call.func << "() length is too long and will be shortened to " << std::to_string(max_strlen) << " bytes (see BPFTRACE_MAX_STRLEN)"; buffer_size = max_buffer_size; } call.type = CreateBuffer(buffer_size); // Consider case : $a = buf("hi", 2); $b = buf("bye", 3); $a == $b // The result of buf is copied to bpf stack. Hence kernel probe read call.type.SetAS(AddrSpace::kernel); } else if (call.func == "ksym" || call.func == "usym") { if (check_nargs(call, 1)) { // allow symbol lookups on casts (eg, function pointers) auto &arg = *call.vargs.at(0); auto &type = arg.type; if (!type.IsIntegerTy() && !type.IsPtrTy()) LOG(ERROR, call.loc, err_) << call.func << "() expects an integer or pointer argument"; } if (call.func == "ksym") call.type = CreateKSym(); else if (call.func == "usym") call.type = CreateUSym(); } else if (call.func == "ntop") { if (!check_varargs(call, 1, 2)) return; auto arg = call.vargs.at(0); if (call.vargs.size() == 2) { arg = call.vargs.at(1); check_arg(call, Type::integer, 0); } if (!arg->type.IsIntTy() && !arg->type.IsStringTy() && !arg->type.IsArrayTy()) LOG(ERROR, call.loc, err_) << call.func << "() expects an integer or array argument, got " << arg->type.GetTy(); // Kind of: // // struct { // int af_type; // union { // char[4] inet4; // char[16] inet6; // } // } int buffer_size = 24; auto type = arg->type; if ((arg->type.IsArrayTy() || arg->type.IsStringTy()) && type.GetSize() != 4 && type.GetSize() != 16) LOG(ERROR, call.loc, err_) << call.func << "() argument must be 4 or 16 bytes in size"; call.type = CreateInet(buffer_size); } else if (call.func == "pton") { if (!check_nargs(call, 1)) return; std::string addr = bpftrace_.get_string_literal(call.vargs.at(0)); int af_type, addr_size; // use '.' and ':' to determine the address family if (addr.find(".") != std::string::npos) { af_type = AF_INET; addr_size = 4; } else if (addr.find(":") != std::string::npos) { af_type = AF_INET6; addr_size = 16; } else { LOG(ERROR, call.loc, err_) << call.func << "() expects an string argument of an IPv4/IPv6 address, got " << addr; return; } std::vector dst(addr_size); auto ret = inet_pton(af_type, addr.c_str(), dst.data()); if (ret != 1) { LOG(ERROR, call.loc, err_) << call.func << "() expects a valid IPv4/IPv6 address, got " << addr; return; } auto elem_type = CreateUInt8(); call.type = CreateArray(addr_size, elem_type); call.type.SetAS(AddrSpace::kernel); call.type.is_internal = true; } else if (call.func == "join") { check_assignment(call, false, false, false); call.type = CreateNone(); if (!check_varargs(call, 1, 2)) return; if (!is_final_pass()) return; auto &arg = *call.vargs.at(0); if (!(arg.type.IsIntTy() || arg.type.IsPtrTy())) { LOG(ERROR, call.loc, err_) << "() only supports int or pointer arguments" << " (" << arg.type.GetTy() << " provided)"; } if (call.vargs.size() > 1) check_arg(call, Type::string, 1, true); } else if (call.func == "reg") { if (check_nargs(call, 1)) { if (check_arg(call, Type::string, 0, true)) { auto reg_name = bpftrace_.get_string_literal(call.vargs.at(0)); int offset = arch::offset(reg_name); ; if (offset == -1) { LOG(ERROR, call.loc, err_) << "'" << reg_name << "' is not a valid register on this architecture" << " (" << arch::name() << ")"; } } } call.type = CreateUInt64(); if (auto probe = dynamic_cast(top_level_node_)) { ProbeType pt = single_provider_type(probe); // In case of different attach_points, Set the addrspace to none. call.type.SetAS(find_addrspace(pt)); } else { // Assume kernel space for data in subprogs call.type.SetAS(AddrSpace::kernel); } } else if (call.func == "kaddr") { if (check_nargs(call, 1)) { check_arg(call, Type::string, 0, true); } call.type = CreateUInt64(); call.type.SetAS(AddrSpace::kernel); } else if (call.func == "percpu_kaddr") { if (check_varargs(call, 1, 2)) { check_arg(call, Type::string, 0, true); if (call.vargs.size() == 2) check_arg(call, Type::integer, 1, false); auto symbol = bpftrace_.get_string_literal(call.vargs.at(0)); if (bpftrace_.btf_->get_var_type(symbol).IsNoneTy()) { LOG(ERROR, call.loc, err_) << "Could not resolve variable \"" << symbol << "\" from BTF"; } } call.type = CreateUInt64(); call.type.SetAS(AddrSpace::kernel); } else if (call.func == "uaddr") { auto probe = get_probe(call.loc, call.func); if (probe == nullptr) return; if (!check_nargs(call, 1)) return; if (!(check_arg(call, Type::string, 0, true) && check_symbol(call, 0))) return; std::vector sizes; auto name = bpftrace_.get_string_literal(call.vargs.at(0)); for (auto *ap : probe->attach_points) { struct symbol sym = {}; int err = bpftrace_.resolve_uname(name, &sym, ap->target); if (err < 0 || sym.address == 0) { LOG(ERROR, call.loc, err_) << "Could not resolve symbol: " << ap->target << ":" << name; } sizes.push_back(sym.size); } for (size_t i = 1; i < sizes.size(); i++) { if (sizes.at(0) != sizes.at(i)) { LOG(ERROR, call.loc, err_) << "Symbol size mismatch between probes. Symbol \"" << name << "\" has size " << sizes.at(0) << " for probe \"" << probe->attach_points.at(0)->name() << "\" but size " << sizes.at(i) << " for probe \"" << probe->attach_points.at(i)->name() << "\""; } } size_t pointee_size = 0; switch (sizes.at(0)) { case 1: case 2: case 4: pointee_size = sizes.at(0) * 8; break; default: pointee_size = 64; } call.type = CreatePointer(CreateInt(pointee_size), AddrSpace::user); } else if (call.func == "cgroupid") { if (check_nargs(call, 1)) { check_arg(call, Type::string, 0, true); } call.type = CreateUInt64(); } else if (call.func == "printf" || call.func == "system" || call.func == "cat" || call.func == "debugf") { check_assignment(call, false, false, false); if (check_varargs(call, 1, 128)) { 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; for (auto iter = call.vargs.begin() + 1; iter != call.vargs.end(); iter++) { // NOTE: modifying the type will break the resizing that happens // in the codegen. We have to copy the type here to avoid modification SizedType ty = (*iter)->type; // Promote to 64-bit if it's not an aggregate type if (!ty.IsAggregate() && !ty.IsTimestampTy()) ty.SetSize(8); args.push_back(Field{ .name = "", .type = ty, .offset = 0, .bitfield = std::nullopt, }); } std::string msg = validate_format_string(fmt.str, args, call.func); if (msg != "") { LOG(ERROR, call.loc, err_) << msg; } } } if (call.func == "debugf" && is_final_pass()) { LOG(WARNING, call.loc, out_) << "The debugf() builtin is not recommended for production use. For " "more information see bpf_trace_printk in bpf-helpers(7)."; } call.type = CreateNone(); } else if (call.func == "exit") { check_assignment(call, false, false, false); if (!check_varargs(call, 0, 1)) return; if (call.vargs.size() == 1) check_arg(call, Type::integer, 0); } else if (call.func == "print") { check_assignment(call, false, false, false); if (in_loop() && is_final_pass() && call.vargs.at(0)->is_map) { LOG(WARNING, call.loc, out_) << "Due to it's asynchronous nature using 'print()' in a loop can " "lead to unexpected behavior. The map will likely be updated " "before the runtime can 'print' it."; } if (check_varargs(call, 1, 3)) { auto &arg = *call.vargs.at(0); if (arg.is_map) { Map &map = static_cast(arg); if (map.key_expr) { if (call.vargs.size() > 1) { LOG(ERROR, call.loc, err_) << "Single-value (i.e. indexed) map " "print cannot take additional " "arguments."; } else if (map.type.IsMultiOutputMapTy()) { LOG(ERROR, call.loc, err_) << "Map type " << map.type << " cannot print the value of individual keys. You must print " "the whole map."; } } if (is_final_pass()) { if (call.vargs.size() > 1) check_arg(call, Type::integer, 1, true); if (call.vargs.size() > 2) check_arg(call, Type::integer, 2, true); if (map.type.IsStatsTy() && call.vargs.size() > 1) { LOG(WARNING, call.loc, out_) << "print()'s top and div arguments are ignored when used on " "stats() maps."; } } } // Note that IsPrintableTy() is somewhat disingenuous here. Printing a // non-map value requires being able to serialize the entire value, so // map-backed types like count(), min(), max(), etc. cannot be printed // through the non-map printing mechanism. // // We rely on the fact that semantic analysis enforces types like count(), // min(), max(), etc. to be assigned directly to a map. This ensures that // the previous `arg.is_map` arm is hit first. else if (arg.type.IsPrintableTy()) { if (call.vargs.size() != 1) LOG(ERROR, call.loc, err_) << "Non-map print() only takes 1 argument, " << call.vargs.size() << " found"; } else { if (is_final_pass()) LOG(ERROR, call.loc, err_) << arg.type << " type passed to " << call.func << "() is not printable"; } } } 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)) { auto &arg = *call.vargs.at(0); if (!arg.is_map) LOG(ERROR, call.loc, err_) << "clear() expects a map to be provided"; else { Map &map = static_cast(arg); if (map.key_expr) { LOG(ERROR, call.loc, err_) << "The map passed to " << call.func << "() should not be " << "indexed by a key"; } } } } else if (call.func == "zero") { check_assignment(call, false, false, false); if (check_nargs(call, 1)) { auto &arg = *call.vargs.at(0); if (!arg.is_map) LOG(ERROR, call.loc, err_) << "zero() expects a map to be provided"; else { Map &map = static_cast(arg); if (map.key_expr) { LOG(ERROR, call.loc, err_) << "The map passed to " << call.func << "() should not be " << "indexed by a key"; } } } } else if (call.func == "len") { if (check_nargs(call, 1)) { auto &arg = *call.vargs.at(0); if (arg.is_map) { Map &map = static_cast(arg); if (map.key_expr) { LOG(ERROR, call.loc, err_) << "The map passed to " << call.func << "() should not be " << "indexed by a key"; } } else if (!arg.type.IsStack()) LOG(ERROR, call.loc, err_) << "len() expects a map or stack to be provided"; call.type = CreateInt64(); } } else if (call.func == "time") { check_assignment(call, false, false, false); if (check_varargs(call, 0, 1)) { if (is_final_pass()) { if (call.vargs.size() > 0) check_arg(call, Type::string, 0, true); } } } else if (call.func == "strftime") { call.type = CreateTimestamp(); if (check_varargs(call, 2, 2) && is_final_pass() && check_arg(call, Type::string, 0, true) && check_arg(call, Type::integer, 1, false)) { auto &arg = *call.vargs.at(1); call.type.ts_mode = arg.type.ts_mode; if (call.type.ts_mode == TimestampMode::monotonic) { LOG(ERROR, call.loc, err_) << "strftime() can not take a monotonic timestamp"; } } } else if (call.func == "kstack") { check_stack_call(call, true); } else if (call.func == "ustack") { check_stack_call(call, false); } else if (call.func == "signal") { if (!bpftrace_.feature_->has_helper_send_signal()) { LOG(ERROR, call.loc, err_) << "BPF_FUNC_send_signal not available for your kernel version"; } check_assignment(call, false, false, false); if (!check_varargs(call, 1, 1)) { return; } auto &arg = *call.vargs.at(0); if (arg.type.IsStringTy() && arg.is_literal) { auto sig = bpftrace_.get_string_literal(&arg); if (signal_name_to_num(sig) < 1) { LOG(ERROR, call.loc, err_) << sig << " is not a valid signal"; } } else if (arg.type.IsIntTy() && arg.is_literal) { auto sig = bpftrace_.get_int_literal(&arg); if (!sig.has_value()) LOG(ERROR, call.loc, err_) << call.func << ": invalid signal value"; else if (*sig < 1 || *sig > 64) { LOG(ERROR, call.loc, err_) << std::to_string(*sig) << " is not a valid signal, allowed range: [1,64]"; } } else if (!arg.type.IsIntTy()) { LOG(ERROR, call.loc, err_) << "signal only accepts string literals or integers"; } } else if (call.func == "path") { auto probe = get_probe(call.loc, call.func); if (probe == nullptr) return; if (!bpftrace_.feature_->has_d_path()) { LOG(ERROR, call.loc, err_) << "BPF_FUNC_d_path not available for your kernel version"; } if (check_varargs(call, 1, 2)) { // Argument for path can be both record and pointer. // It's pointer when it's passed directly from the probe // argument, like: path(args.path)) // It's record when it's referenced as object pointer // member, like: path(args.filp->f_path)) if (!check_arg(call, Type::record, 0, false, false) && !check_arg(call, Type::pointer, 0, false, false)) { auto &arg = *call.vargs.at(0); LOG(ERROR, call.loc, err_) << "path() only supports pointer or record argument (" << arg.type.GetTy() << " provided)"; } auto call_type_size = bpftrace_.config_.get(ConfigKeyInt::max_strlen); if (call.vargs.size() == 2) { if (check_arg(call, Type::integer, 1, true)) { auto size = bpftrace_.get_int_literal(call.vargs.at(1)); if (size.has_value()) { if (size < 0) LOG(ERROR, call.loc, err_) << "Builtin path requires a non-negative size"; call_type_size = size.value(); } else { LOG(ERROR, call.loc, err_) << call.func << ": invalid size value"; } } } call.type = SizedType(Type::string, call_type_size); } for (auto *attach_point : probe->attach_points) { ProbeType type = probetype(attach_point->provider); if (type != ProbeType::fentry && type != ProbeType::fexit && type != ProbeType::iter) LOG(ERROR, call.loc, err_) << "The path function can only be used with " << "'fentry', 'fexit', 'iter' probes"; } } else if (call.func == "strerror") { call.type = CreateStrerror(); if (check_nargs(call, 1)) check_arg(call, Type::integer, 0, false); } else if (call.func == "strncmp") { if (check_nargs(call, 3)) { check_arg(call, Type::string, 0); check_arg(call, Type::string, 1); if (check_arg(call, Type::integer, 2, true)) { auto size = bpftrace_.get_int_literal(call.vargs.at(2)); if (size.has_value()) { if (size < 0) LOG(ERROR, call.loc, err_) << "Builtin strncmp requires a non-negative size"; } else LOG(ERROR, call.loc, err_) << call.func << ": invalid size value"; } } call.type = CreateUInt64(); } else if (call.func == "strcontains") { static constexpr auto warning = R"( strcontains() is known to have verifier complexity issues when the product of both string sizes is larger than ~2000 bytes. If you're seeing errors, try clamping the string sizes. For example: * `str($ptr, 16)` * `path($ptr, 16)` )"; if (check_nargs(call, 2)) { check_arg(call, Type::string, 0); check_arg(call, Type::string, 1); if (is_final_pass()) { auto arg0_sz = call.vargs.at(0)->type.GetSize(); auto arg1_sz = call.vargs.at(1)->type.GetSize(); if (arg0_sz * arg1_sz > 2000) { LOG(WARNING, call.loc, out_) << warning; } } } call.type = CreateUInt64(); } else if (call.func == "override") { auto probe = get_probe(call.loc, call.func); if (probe == nullptr) return; if (!bpftrace_.feature_->has_helper_override_return()) { LOG(ERROR, call.loc, err_) << "BPF_FUNC_override_return not available for your kernel version"; } check_assignment(call, false, false, false); if (check_varargs(call, 1, 1)) { check_arg(call, Type::integer, 0, false); } for (auto *attach_point : probe->attach_points) { ProbeType type = probetype(attach_point->provider); if (type != ProbeType::kprobe) { LOG(ERROR, call.loc, err_) << call.func << " can only be used with kprobes."; } } } else if (call.func == "kptr" || call.func == "uptr") { if (!check_nargs(call, 1)) return; // kptr should accept both integer or pointer. Consider case: kptr($1) auto &arg = *call.vargs.at(0); if (!arg.type.IsIntTy() && !arg.type.IsPtrTy()) { LOG(ERROR, call.loc, err_) << call.func << "() only supports " << "integer or pointer arguments (" << arg.type.GetTy() << " provided)"; return; } auto as = (call.func == "kptr" ? AddrSpace::kernel : AddrSpace::user); call.type = call.vargs.front()->type; call.type.SetAS(as); } else if (call.func == "macaddr") { if (!check_nargs(call, 1)) return; auto &arg = call.vargs.at(0); if (!arg->type.IsIntTy() && !arg->type.IsArrayTy() && !arg->type.IsByteArray() && !arg->type.IsPtrTy()) LOG(ERROR, call.loc, err_) << call.func << "() only supports array or pointer arguments" << " (" << arg->type.GetTy() << " provided)"; auto type = arg->type; if ((type.IsArrayTy() || type.IsByteArray()) && type.GetSize() != 6) 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") { if (check_nargs(call, 1)) check_arg(call, Type::integer, 0); // 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.IsIntTy()) { LOG(ERROR, call.loc, err_) << call.func << "() only supports integer arguments (" << arg->type.GetTy() << " provided)"; return; } call.type = CreateUInt(arg->type.GetIntBitWidth()); } else if (call.func == "skboutput") { if (!bpftrace_.feature_->has_skb_output()) { LOG(ERROR, call.loc, err_) << "BPF_FUNC_skb_output is not available for your kernel " "version"; } check_assignment(call, false, true, false); if (check_nargs(call, 4)) { if (is_final_pass()) { // pcap file name check_arg(call, Type::string, 0, true); // *skb check_arg(call, Type::pointer, 1, false); // cap length check_arg(call, Type::integer, 2, false); // cap offset, default is 0 // some tracepoints like dev_queue_xmit will output ethernet header, set // offset to 14 bytes can exclude this header check_arg(call, Type::integer, 3, false); } } call.type = CreateUInt32(); } else if (call.func == "nsecs") { if (check_varargs(call, 0, 1)) { call.type = CreateUInt64(); call.type.ts_mode = TimestampMode::boot; if (call.vargs.size() == 1 && check_arg(call, Type::timestamp_mode, 0)) { call.type.ts_mode = call.vargs.at(0)->type.ts_mode; } if (call.type.ts_mode == TimestampMode::tai && !bpftrace_.feature_->has_helper_ktime_get_tai_ns()) { LOG(ERROR, call.loc, err_) << "Kernel does not support tai timestamp, please try sw_tai"; } if (call.type.ts_mode == TimestampMode::sw_tai && !bpftrace_.delta_taitime_.has_value()) { LOG(ERROR, call.loc, err_) << "Failed to initialize sw_tai in " "userspace. This is very unexpected."; } } } else { LOG(ERROR, call.loc, err_) << "Unknown function: '" << call.func << "'"; call.type = CreateNone(); } } void SemanticAnalyser::visit(Sizeof &szof) { szof.type = CreateUInt64(); if (szof.expr) { szof.expr = dereference_if_needed(szof.expr); szof.argtype = szof.expr->type; } resolve_struct_type(szof.argtype, szof.loc); } void SemanticAnalyser::visit(Offsetof &offof) { offof.type = CreateUInt64(); if (offof.expr) { offof.expr = dereference_if_needed(offof.expr); offof.record = offof.expr->type; } resolve_struct_type(offof.record, offof.loc); // Check if all sub-fields are present. SizedType record = offof.record; for (const auto &field : offof.field) { if (!record.IsRecordTy()) { LOG(ERROR, offof.loc, err_) << "'" << record << "' " << "is not a record type."; } else if (!bpftrace_.structs.Has(record.GetName())) { LOG(ERROR, offof.loc, err_) << "'" << record.GetName() << "' does not exist."; } else if (!record.HasField(field)) { LOG(ERROR, offof.loc, err_) << "'" << record.GetName() << "' " << "has no field named " << "'" << field << "'"; } else { // Get next sub-field record = record.GetField(field).type; } } } void SemanticAnalyser::check_stack_call(Call &call, bool kernel) { call.type = CreateStack(kernel); if (!check_varargs(call, 0, 2)) { return; } StackType stack_type; stack_type.mode = bpftrace_.config_.get(ConfigKeyStackMode::default_); switch (call.vargs.size()) { case 0: break; case 1: { auto &arg = *call.vargs.at(0); // If we have a single argument it can be either // stack-mode or stack-size if (arg.type.IsStackModeTy()) { if (check_arg(call, Type::stack_mode, 0, true)) stack_type.mode = static_cast(arg).type.stack_type.mode; } else { if (check_arg(call, Type::integer, 0, true)) { auto limit = bpftrace_.get_int_literal(&arg); if (limit.has_value()) stack_type.limit = *limit; else LOG(ERROR, call.loc, err_) << call.func << ": invalid limit value"; } } break; } case 2: { if (check_arg(call, Type::stack_mode, 0, true)) { auto &mode_arg = *call.vargs.at(0); stack_type.mode = static_cast(mode_arg).type.stack_type.mode; } if (check_arg(call, Type::integer, 1, true)) { auto &limit_arg = call.vargs.at(1); auto limit = bpftrace_.get_int_literal(limit_arg); if (limit.has_value()) stack_type.limit = *limit; else LOG(ERROR, call.loc, err_) << call.func << ": invalid limit value"; } break; } default: LOG(ERROR, call.loc, err_) << "Invalid number of arguments"; break; } if (stack_type.limit > MAX_STACK_SIZE) { LOG(ERROR, call.loc, err_) << call.func << "([int limit]): limit shouldn't exceed " << MAX_STACK_SIZE << ", " << stack_type.limit << " given"; } call.type = CreateStack(kernel, stack_type); } Probe *SemanticAnalyser::get_probe(const location &loc, std::string name) { auto probe = dynamic_cast(top_level_node_); if (probe == nullptr) { // Attempting to use probe-specific feature in non-probe context if (name.empty()) { LOG(ERROR, loc, err_) << "Feature not supported outside probe"; } else { LOG(ERROR, loc, err_) << "Builtin " << name << " not supported outside probe"; } } return probe; } void SemanticAnalyser::validate_map_key(const SizedType &key, const location &loc) { if (key.IsPtrTy() && key.IsCtxAccess()) { // map functions only accepts a pointer to a element in the stack LOG(ERROR, loc, err_) << "context cannot be used as a map key"; } if (key.IsHistTy() || key.IsLhistTy() || key.IsStatsTy()) { LOG(ERROR, loc, err_) << key << " cannot be used as a map key"; } if (is_final_pass() && key.IsNoneTy()) { LOG(ERROR, loc, err_) << "Invalid expression for assignment: "; } } void SemanticAnalyser::visit(Map &map) { SizedType new_key_type = CreateNone(); bool key_is_map = false; if (map.key_expr) { map.key_expr = dereference_if_needed(map.key_expr); key_is_map = map.key_expr->is_map; new_key_type = create_key_type(map.key_expr->type, map.key_expr->loc); } if (!map.skip_key_validation) { if (const auto &key = map_key_.find(map.ident); key != map_key_.end()) { if (map.key_expr) { update_current_key(key->second, new_key_type); validate_new_key(key->second, new_key_type, map.ident, map.loc); } else { if (!key->second.IsNoneTy()) { LOG(ERROR, map.loc, err_) << "Argument mismatch for " << map.ident << ": " << "trying to access with no arguments when map expects " "arguments: " "'" << key->second << "'"; } } } else { // If the key used is a map, we might not have the type of it yet // e.g. `BEGIN { @mymap[@i] = "hello"; @i = 1; }` if (!key_is_map || !new_key_type.IsNoneTy()) { map_key_.insert({ map.ident, new_key_type }); } } } auto search_val = map_val_.find(map.ident); if (search_val != map_val_.end()) { map.type = search_val->second; if (map.is_read && map.type.IsCastableMapTy() && !bpftrace_.feature_->has_helper_map_lookup_percpu_elem()) { LOG(ERROR, map.loc, err_) << "Missing required kernel feature: map_lookup_percpu_elem"; } } else { // If there is no record of any assignment after the first pass // then it's safe to say this map is undefined if (!is_first_pass()) { LOG(ERROR, map.loc, err_) << "Undefined map: " << map.ident; } pass_tracker_.inc_num_unresolved(); map.type = CreateNone(); } auto map_key_search_val = map_key_.find(map.ident); if (map_key_search_val != map_key_.end()) { map.key_type = map_key_search_val->second; } else { map.key_type = CreateNone(); } } void SemanticAnalyser::visit(Variable &var) { if (auto *found = find_variable(var.ident)) { var.type = found->type; if (!found->was_assigned) { LOG(WARNING, var.loc, out_) << "Variable used before it was assigned: " << var.ident; } return; } LOG(ERROR, var.loc, err_) << "Undefined or undeclared variable: " << var.ident; var.type = CreateNone(); } void SemanticAnalyser::visit(ArrayAccess &arr) { arr.expr = dereference_if_needed(arr.expr); arr.indexpr = dereference_if_needed(arr.indexpr); SizedType &type = arr.expr->type; SizedType &indextype = arr.indexpr->type; if (is_final_pass()) { if (!type.IsArrayTy() && !type.IsPtrTy()) { LOG(ERROR, arr.loc, err_) << "The array index operator [] can only be " "used on arrays and pointers, found " << type.GetTy() << "."; return; } if (type.IsPtrTy() && type.GetPointeeTy()->GetSize() == 0) { LOG(ERROR, arr.loc, err_) << "The array index operator [] cannot be used " "on a pointer to an unsized type (void *)."; } if (indextype.IsIntTy() && arr.indexpr->is_literal) { if (type.IsArrayTy()) { auto index = bpftrace_.get_int_literal(arr.indexpr); if (index.has_value()) { size_t num = type.GetNumElements(); if (num != 0 && static_cast(*index) >= num) LOG(ERROR, arr.loc, err_) << "the index " << *index << " is out of bounds for array of size " << num; } else LOG(ERROR, arr.loc, err_) << "invalid index expression"; } } else { LOG(ERROR, arr.loc, err_) << "The array index operator [] only " "accepts literal integer indices."; } } if (type.IsArrayTy()) arr.type = *type.GetElementTy(); else if (type.IsPtrTy()) arr.type = *type.GetPointeeTy(); else arr.type = CreateNone(); arr.type.is_internal = type.is_internal; arr.type.SetAS(type.GetAS()); // BPF verifier cannot track BTF information for double pointers so we cannot // propagate is_btftype for arrays of pointers and we need to reset it on the // array type as well. Indexing a pointer as an array also can't be verified, // so the same applies there. if (arr.type.IsPtrTy() || type.IsPtrTy()) type.is_btftype = false; arr.type.is_btftype = type.is_btftype; } void SemanticAnalyser::binop_int(Binop &binop) { bool lsign = binop.left->type.IsSigned(); bool rsign = binop.right->type.IsSigned(); auto left = binop.left; auto right = binop.right; auto left_literal = bpftrace_.get_int_literal(left); auto right_literal = bpftrace_.get_int_literal(right); // First check if operand signedness is the same if (lsign != rsign) { // Convert operands to unsigned if it helps make (lsign == rsign) // // For example: // // unsigned int a; // if (a > 10) ...; // // No warning should be emitted as we know that 10 can be // represented as unsigned int if (lsign && !rsign && left_literal && *left_literal >= 0) { lsign = false; } // The reverse (10 < a) should also hold else if (!lsign && rsign && right_literal && *right_literal >= 0) { rsign = false; } else { switch (binop.op) { case Operator::EQ: case Operator::NE: case Operator::LE: case Operator::GE: case Operator::LT: case Operator::GT: LOG(WARNING, binop.loc, out_) << "comparison of integers of different signs: '" << left->type << "' and '" << right->type << "'" << " can lead to undefined behavior"; break; case Operator::PLUS: case Operator::MINUS: case Operator::MUL: case Operator::DIV: case Operator::MOD: LOG(WARNING, binop.loc, out_) << "arithmetic on integers of different signs: '" << left->type << "' and '" << right->type << "'" << " can lead to undefined behavior"; break; default: break; } } } // Next, warn on any operations that require signed division. // // SDIV is not implemented for bpf. See Documentation/bpf/bpf_design_QA // in kernel sources if (binop.op == Operator::DIV || binop.op == Operator::MOD) { // Convert operands to unsigned if possible if (lsign && left_literal && *left_literal >= 0) lsign = false; if (rsign && right_literal && *right_literal >= 0) rsign = false; // If they're still signed, we have to warn if (lsign || rsign) { LOG(WARNING, binop.loc, out_) << "signed operands for '" << opstr(binop) << "' can lead to undefined behavior " << "(cast to unsigned to silence warning)"; } } if (func_ == "str") { // Check if one of the operands is a positional parameter // The other one should be a constant offset auto pos_param = dynamic_cast(left); auto offset = dynamic_cast(right); if (!pos_param) { pos_param = dynamic_cast(right); offset = dynamic_cast(left); } if (pos_param) { auto len = bpftrace_.get_param(pos_param->n, true).length(); if (!offset || binop.op != Operator::PLUS || offset->n < 0 || static_cast(offset->n) > len) { LOG(ERROR, binop.loc + binop.right->loc, err_) << "only addition of a single constant less or equal to the " << "length of $" << pos_param->n << " (which is " << len << ")" << " is allowed inside str()"; } } } } void SemanticAnalyser::binop_array(Binop &binop) { auto &lht = binop.left->type; auto &rht = binop.right->type; if (binop.op != Operator::EQ && binop.op != Operator::NE) { LOG(ERROR, binop.loc, err_) << "The " << opstr(binop) << " operator cannot be used on arrays."; } if (lht.GetNumElements() != rht.GetNumElements()) { LOG(ERROR, binop.loc, err_) << "Only arrays of same size support comparison operators."; } if (!lht.GetElementTy()->IsIntegerTy() || lht != rht) { LOG(ERROR, binop.loc, err_) << "Only arrays of same sized integer support comparison operators."; } } void SemanticAnalyser::binop_ptr(Binop &binop) { auto &lht = binop.left->type; auto &rht = binop.right->type; bool left_is_ptr = lht.IsPtrTy(); auto &ptr = left_is_ptr ? lht : rht; auto &other = left_is_ptr ? rht : lht; bool compare = false; bool logical = false; // Do what C does switch (binop.op) { case Operator::EQ: case Operator::NE: case Operator::LE: case Operator::GE: case Operator::LT: case Operator::GT: compare = true; break; case Operator::LAND: case Operator::LOR: logical = true; break; default:; } auto invalid_op = [&binop, this, &lht, &rht]() { LOG(ERROR, binop.loc, err_) << "The " << opstr(binop) << " operator can not be used on expressions of types " << lht << ", " << rht; }; // Binop on two pointers if (other.IsPtrTy()) { if (compare) { binop.type = CreateUInt(64); if (is_final_pass()) { auto le = lht.GetPointeeTy(); auto re = rht.GetPointeeTy(); if (*le != *re) { LOG(WARNING, binop.left->loc + binop.right->loc, out_) << "comparison of distinct pointer types ('" << *le << ", '" << *re << "')"; } } } else if (logical) { binop.type = CreateUInt(64); } else { invalid_op(); } } // Binop on a pointer and int else if (other.IsIntTy()) { // sum is associative but minus only works with pointer on the left hand // side if (binop.op == Operator::MINUS && !left_is_ptr) invalid_op(); else if (binop.op == Operator::PLUS || binop.op == Operator::MINUS) binop.type = CreatePointer(*ptr.GetPointeeTy(), ptr.GetAS()); else if (compare || logical) binop.type = CreateInt(64); else invalid_op(); } // Might need an additional pass to resolve the type else if (other.IsNoneTy()) { if (is_final_pass()) { invalid_op(); } } // Binop on a pointer and something else else { invalid_op(); } } void SemanticAnalyser::visit(Binop &binop) { binop.left = dereference_if_needed(binop.left); binop.right = dereference_if_needed(binop.right); auto &lht = binop.left->type; auto &rht = binop.right->type; bool lsign = binop.left->type.IsSigned(); bool rsign = binop.right->type.IsSigned(); bool is_int_binop = (lht.IsCastableMapTy() || lht.IsIntTy()) && (rht.IsCastableMapTy() || rht.IsIntTy()); if (lht.IsPtrTy() || rht.IsPtrTy()) { binop_ptr(binop); return; } bool is_signed = lsign && rsign; switch (binop.op) { case Operator::LEFT: case Operator::RIGHT: is_signed = lsign; break; default: break; } if (is_int_binop) { // Implicit size promotion to larger of the two auto size = std::max(lht.GetSize(), rht.GetSize()); binop.type = CreateInteger(size * 8, is_signed); } else { // Default type - will be overriden below as necessary binop.type = CreateInteger(64, is_signed); } auto addr_lhs = binop.left->type.GetAS(); auto addr_rhs = binop.right->type.GetAS(); // if lhs or rhs has different addrspace (not none), then set the // addrspace to none. This preserves the behaviour for x86. if (addr_lhs != addr_rhs && addr_lhs != AddrSpace::none && addr_rhs != AddrSpace::none) { if (is_final_pass()) LOG(WARNING, binop.loc, out_) << "Addrspace mismatch"; binop.type.SetAS(AddrSpace::none); } // Associativity from left to right for binary operator else if (addr_lhs != AddrSpace::none) { binop.type.SetAS(addr_lhs); } else { // In case rhs is none, then this triggers warning in selectProbeReadHelper. binop.type.SetAS(addr_rhs); } if (!is_final_pass()) { return; } if (is_int_binop) { binop_int(binop); } else if (lht.IsArrayTy() && rht.IsArrayTy()) { binop_array(binop); } else if (lht.IsPtrTy() || rht.IsPtrTy()) { // This case is caught earlier, just here for readability of the if/else // flow } // Compare type here, not the sized type as we it needs to work on strings of // different lengths else if (lht.GetTy() != rht.GetTy()) { LOG(ERROR, binop.left->loc + binop.right->loc, err_) << "Type mismatch for '" << opstr(binop) << "': comparing '" << lht << "' with '" << rht << "'"; } // Also allow combination like reg("sp") + 8 else if (binop.op != Operator::EQ && binop.op != Operator::NE) { LOG(ERROR, binop.loc, err_) << "The " << opstr(binop) << " operator can not be used on expressions of types " << lht << ", " << rht; } else if (binop.op == Operator::EQ && ((!binop.left->is_literal && binop.right->is_literal) || (binop.left->is_literal && !binop.right->is_literal))) { auto *lit = binop.left->is_literal ? binop.left : binop.right; auto *str = lit == binop.left ? binop.right : binop.left; auto lit_len = bpftrace_.get_string_literal(lit).size(); auto str_len = str->type.GetSize(); if (lit_len > str_len) { LOG(WARNING, binop.left->loc + binop.loc + binop.right->loc, out_) << "The literal is longer than the variable string (size=" << str_len << "), condition will always be false"; } } } void SemanticAnalyser::visit(Unop &unop) { if (unop.op == Operator::INCREMENT || unop.op == Operator::DECREMENT) { // Handle ++ and -- before visiting unop.expr, because these // operators should be able to work with undefined maps. if (!unop.expr->is_map && !unop.expr->is_variable) { LOG(ERROR, unop.loc, err_) << "The " << opstr(unop) << " operator must be applied to a map or variable"; } if (unop.expr->is_map) { Map &map = static_cast(*unop.expr); auto *maptype = get_map_type(map); if (!maptype) assign_map_type(map, CreateInt64()); } } unop.expr = dereference_if_needed(unop.expr); auto valid_ptr_op = false; switch (unop.op) { case Operator::INCREMENT: case Operator::DECREMENT: case Operator::MUL: valid_ptr_op = true; break; default:; } SizedType &type = unop.expr->type; if (is_final_pass()) { // Unops are only allowed on ints (e.g. ~$x), dereference only on pointers // and context (we allow args->field for backwards compatibility) // References are not allowed, instead they get turned into pointers by the // `dereference_if_needed()`. if (!type.IsIntegerTy() && !((type.IsPtrTy() || type.IsCtxAccess()) && valid_ptr_op)) { LOG(ERROR, unop.loc, err_) << "The " << opstr(unop) << " operator can not be used on expressions of type '" << type << "'"; } } if (unop.op == Operator::MUL) { if (type.IsPtrTy()) { unop.type = SizedType(*type.GetPointeeTy()); if (type.IsCtxAccess()) unop.type.MarkCtxAccess(); unop.type.is_internal = type.is_internal; unop.type.SetAS(type.GetAS()); // BPF verifier cannot track BTF information for double pointers if (!unop.type.IsPtrTy()) unop.type.is_btftype = type.is_btftype; } else if (type.IsRecordTy()) { // We allow dereferencing "args" with no effect (for backwards compat) if (type.IsCtxAccess()) unop.type = type; else { LOG(ERROR, unop.loc, err_) << "Can not dereference struct/union of type '" << type.GetName() << "'. It is not a pointer."; } } else if (type.IsIntTy()) { unop.type = CreateUInt64(); } } else if (unop.op == Operator::LNOT) { // CreateUInt() abort if a size is invalid, so check the size here if (!(type.GetSize() == 0 || type.GetSize() == 1 || type.GetSize() == 2 || type.GetSize() == 4 || type.GetSize() == 8)) { LOG(ERROR, unop.loc, err_) << "The " << opstr(unop) << " operator can not be used on expressions of type '" << type << "'"; } else { unop.type = CreateUInt(8 * type.GetSize()); } } else if (type.IsPtrTy() && valid_ptr_op) { unop.type = unop.expr->type; } else { unop.type = CreateInteger(64, type.IsSigned()); } } void SemanticAnalyser::visit(Ternary &ternary) { ternary.cond = dereference_if_needed(ternary.cond); ternary.left = dereference_if_needed(ternary.left); ternary.right = dereference_if_needed(ternary.right); const Type &cond = ternary.cond->type.GetTy(); const auto &lhs = ternary.left->type; const auto &rhs = ternary.right->type; if (!lhs.IsSameType(rhs)) { if (is_final_pass()) { LOG(ERROR, ternary.loc, err_) << "Ternary operator must return the same type: " << "have '" << lhs << "' and '" << rhs << "'"; } // This assignment is just temporary to prevent errors // before the final pass ternary.type = lhs; return; } if (ternary.left->type.IsStack() && ternary.left->type.stack_type != ternary.right->type.stack_type) { // TODO: fix this for different stack types LOG(ERROR, ternary.loc, err_) << "Ternary operator must have the same stack type on the right " "and left sides."; return; } if (is_final_pass() && cond != Type::integer && cond != Type::pointer) { LOG(ERROR, ternary.loc, err_) << "Invalid condition in ternary: " << cond; return; } if (lhs.IsIntegerTy()) { ternary.type = CreateInteger(64, ternary.left->type.IsSigned()); } else { auto lsize = ternary.left->type.GetSize(); auto rsize = ternary.right->type.GetSize(); if (lhs.IsTupleTy()) { ternary.type = create_merged_tuple(ternary.right->type, ternary.left->type); } else { ternary.type = lsize > rsize ? ternary.left->type : ternary.right->type; } } } void SemanticAnalyser::visit(If &if_node) { if_node.cond = dereference_if_needed(if_node.cond); if (is_final_pass()) { const Type &cond = if_node.cond->type.GetTy(); if (cond != Type::integer && cond != Type::pointer) LOG(ERROR, if_node.loc, err_) << "Invalid condition in if(): " << cond; } visit(if_node.if_block); visit(if_node.else_block); } void SemanticAnalyser::visit(Unroll &unroll) { unroll.expr = dereference_if_needed(unroll.expr); auto unroll_value = bpftrace_.get_int_literal(unroll.expr); if (!unroll_value.has_value()) { LOG(ERROR, unroll.loc, err_) << "invalid unroll value"; return; } unroll.var = *unroll_value; if (unroll.var > 100) { LOG(ERROR, unroll.loc, err_) << "unroll maximum value is 100"; } else if (unroll.var < 1) { LOG(ERROR, unroll.loc, err_) << "unroll minimum value is 1"; } visit(unroll.block); } void SemanticAnalyser::visit(Jump &jump) { switch (jump.ident) { case JumpType::RETURN: if (jump.return_value) { jump.return_value = dereference_if_needed(jump.return_value); } if (auto subprog = dynamic_cast(top_level_node_)) { if ((subprog->return_type.IsVoidTy() != (jump.return_value == nullptr)) || (jump.return_value && jump.return_value->type != subprog->return_type)) { LOG(ERROR, jump.loc, err_) << "Function " << subprog->name() << " is of type " << subprog->return_type << ", cannot return " << (jump.return_value ? jump.return_value->type : CreateVoid()); } } break; case JumpType::BREAK: case JumpType::CONTINUE: if (!in_loop()) LOG(ERROR, jump.loc, err_) << opstr(jump) << " used outside of a loop"; break; default: LOG(ERROR, jump.loc, err_) << "Unknown jump: '" << opstr(jump) << "'"; } } void SemanticAnalyser::visit(While &while_block) { while_block.cond = dereference_if_needed(while_block.cond); loop_depth_++; visit(while_block.block); loop_depth_--; } void SemanticAnalyser::visit(For &f) { if (!bpftrace_.feature_->has_helper_for_each_map_elem()) { LOG(ERROR, f.loc, err_) << "Missing required kernel feature: for_each_map_elem"; } // For-loops are implemented using the bpf_for_each_map_elem helper function, // which requires them to be rewritten into a callback style. // // Pseudo code for the transformation we apply: // // Before: // PROBE { // @map[0] = 1; // for ($kv : @map) { // [LOOP BODY] // } // } // // After: // PROBE { // @map[0] = 1; // bpf_for_each_map_elem(@map, &map_for_each_cb, 0, 0); // } // long map_for_each_cb(bpf_map *map, // const void *key, // void *value, // void *ctx) { // $kv = ((uint64)key, (uint64)value); // [LOOP BODY] // } // // // To allow variables to be shared between the loop callback and the main // program, some extra steps are taken: // // 1. Determine which variables need to be shared with the loop callback // 2. Pack pointers to them into a context struct // 3. Pass pointer to the context struct to the callback function // 4. In the callback, override the shared variables so that they read and // write through the context pointers instead of directly from their // original addresses // // Example transformation with context: // // Before: // PROBE { // $str = "hello"; // $not_shared = 2; // $len = 0; // @map[11, 12] = "c"; // for ($kv : @map) { // print($str); // $len++; // } // print($len); // print($not_shared); // } // // After: // struct ctx_t { // string *str; // uint64 *len; // }; // PROBE { // $str = "hello"; // $not_shared = 2; // $len = 0; // @map[11, 12] = "c"; // // ctx_t ctx { .str = &$str, .len = &$len }; // bpf_for_each_map_elem(@map, &map_for_each_cb, &ctx, 0); // // print($len); // print($not_shared); // } // long map_for_each_cb(bpf_map *map, // const void *key, // void *value, // void *ctx) { // $kv = (((uint64, uint64))key, (string)value); // $str = ((ctx_t*)ctx)->str; // $len = ((ctx_t*)ctx)->len; // // print($str); // $len++; // } // Validate decl const auto &decl_name = f.decl->ident; if (find_variable(decl_name)) { LOG(ERROR, f.decl->loc, err_) << "Loop declaration shadows existing variable: " + decl_name; } // Validate expr if (!f.expr->is_map) { LOG(ERROR, f.expr->loc, err_) << "Loop expression must be a map"; return; } Map &map = static_cast(*f.expr); if (!map.type.IsMapIterableTy()) { LOG(ERROR, f.expr->loc, err_) << "Loop expression does not support type: " << map.type; return; } // Validate body // This could be relaxed in the future: CollectNodes jumps(ctx_); jumps.visit(f.stmts); for (const Jump &n : jumps.nodes()) { LOG(ERROR, n.loc, err_) << "'" << opstr(n) << "' statement is not allowed in a for-loop"; } map.skip_key_validation = true; visit(map); if (has_error()) return; // Collect a list of unique variables which are referenced in the loop's body // and declared before the loop. These will be passed into the loop callback // function as the context parameter. std::unordered_set found_vars; // Only do this on the first pass because variables declared later // in a script will get added to the outer scope, which these do not // reference e.g. // BEGIN { @a[1] = 1; for ($kv : @a) { $x = 2; } let $x; } if (is_first_pass()) { for (auto *stmt : f.stmts) { // We save these for potential use at the end of this function in // subsequent passes in case the map we're iterating over isn't ready // yet and still needs additional passes to resolve its key/value types // e.g. BEGIN { $x = 1; for ($kv : @a) { print(($x)); } @a[1] = 1; } // // This is especially tricky because we need to visit all statements // inside the for loop to get the types of the referenced variables but // only after we have the map's key/value type so we can also check // the usages of the created $kv tuple variable. auto [iter, _] = for_vars_referenced_.try_emplace(&f, ctx_); auto &collector = iter->second; collector.visit(stmt, [this, &found_vars](const auto &var) { if (found_vars.find(var.ident) != found_vars.end()) return false; if (find_variable(var.ident)) { found_vars.insert(var.ident); return true; } return false; }); } } // Create type for the loop's decl // Iterating over a map provides a tuple: (map_key, map_val) auto *mapkey = get_map_key_type(map); auto *mapval = get_map_type(map); if (!mapval) return; if (!mapkey || mapkey->IsNoneTy()) { if (is_final_pass()) { LOG(ERROR, map.loc, err_) << "Maps used as for-loop expressions must have keys to iterate over"; } return; } f.decl->type = CreateTuple(bpftrace_.structs.AddTuple({ *mapkey, *mapval })); scope_stack_.push_back(&f); variables_[scope_stack_.back()][decl_name] = { .type = f.decl->type, .can_resize = true, .was_assigned = true }; loop_depth_++; accept_statements(f.stmts); loop_depth_--; scope_stack_.pop_back(); // Currently, we do not pass BPF context to the callback so disable builtins // which require ctx access. CollectNodes builtins(ctx_); builtins.visit(f.stmts); for (const Builtin &builtin : builtins.nodes()) { if (builtin.type.IsCtxAccess() || builtin.is_argx() || builtin.ident == "retval") { LOG(ERROR, builtin.loc, err_) << "'" << builtin.ident << "' builtin is not allowed in a for-loop"; } } // Finally, create the context tuple now that all variables inside the loop // have been visited. std::vector ctx_types; std::vector ctx_idents; auto [iter, _] = for_vars_referenced_.try_emplace(&f, ctx_); auto &collector = iter->second; for (const Variable &var : collector.nodes()) { ctx_types.push_back(CreatePointer(var.type, AddrSpace::bpf)); ctx_idents.push_back(var.ident); } f.ctx_type = CreateRecord( "", bpftrace_.structs.AddAnonymousStruct(ctx_types, ctx_idents)); } void SemanticAnalyser::visit(FieldAccess &acc) { // A field access must have a field XOR index assert((acc.field.size() > 0) != (acc.index >= 0)); acc.expr = dereference_if_needed(acc.expr); SizedType &type = acc.expr->type; if (type.IsPtrTy()) { LOG(ERROR, acc.loc, err_) << "Can not access field '" << acc.field << "' on type '" << type << "'. Try dereferencing it first, or using '->'"; return; } if (!type.IsRecordTy() && !type.IsTupleTy()) { if (is_final_pass()) { std::string field; if (acc.field.size()) field += "field '" + acc.field + "'"; else field += "index " + std::to_string(acc.index); LOG(ERROR, acc.loc, err_) << "Can not access " << field << " on expression of type '" << type << "'"; } return; } if (type.is_funcarg) { auto probe = get_probe(acc.loc); if (probe == nullptr) return; auto arg = bpftrace_.structs.GetProbeArg(*probe, acc.field); if (arg) { // Only set acc.type if it's not already set. [1] // Overwriting the type once set could override the conversion // from Reference to Pointer done by dereference_if_needed. if (acc.type.IsNoneTy()) acc.type = arg->type; acc.type.SetAS(acc.expr->type.GetAS()); if (is_final_pass()) { if (acc.type.IsNoneTy()) LOG(ERROR, acc.loc, err_) << acc.field << " has unsupported type"; ProbeType probetype = single_provider_type(probe); if (probetype == ProbeType::fentry || probetype == ProbeType::fexit) { acc.type.is_btftype = true; } } } else { LOG(ERROR, acc.loc, err_) << "Can't find function parameter " << acc.field; } return; } if (type.IsTupleTy()) { if (acc.index < 0) { LOG(ERROR, acc.loc, err_) << "Tuples must be indexed with a constant and non-negative integer"; return; } bool valid_idx = static_cast(acc.index) < type.GetFields().size(); // We may not have inferred the full type of the tuple yet in early passes // so wait until the final pass. if (!valid_idx && is_final_pass()) LOG(ERROR, acc.loc, err_) << "Invalid tuple index: " << acc.index << ". Found " << type.GetFields().size() << " elements in tuple."; if (valid_idx) acc.type = type.GetField(acc.index).type; return; } if (!bpftrace_.structs.Has(type.GetName())) { LOG(ERROR, acc.loc, err_) << "Unknown struct/union: '" << type.GetName() << "'"; return; } std::map> structs; if (type.is_tparg) { auto probe = get_probe(acc.loc); if (probe == nullptr) return; for (AttachPoint *attach_point : probe->attach_points) { if (probetype(attach_point->provider) != ProbeType::tracepoint) { // The args builtin can only be used with tracepoint // an error message is already generated in visit(Builtin) // just continue semantic analysis continue; } auto matches = bpftrace_.probe_matcher_->get_matches_for_ap( *attach_point); for (auto &match : matches) { std::string tracepoint_struct = TracepointFormatParser::get_struct_name( match); structs[tracepoint_struct] = bpftrace_.structs.Lookup( tracepoint_struct); } } } else { structs[type.GetName()] = type.GetStruct(); } for (auto it : structs) { std::string cast_type = it.first; const auto record = it.second.lock(); if (!record->HasField(acc.field)) { LOG(ERROR, acc.loc, err_) << "Struct/union of type '" << cast_type << "' does not contain " << "a field named '" << acc.field << "'"; } else { const auto &field = record->GetField(acc.field); if (field.type.IsPtrTy()) { const auto &tags = field.type.GetBtfTypeTags(); // Currently only "rcu" is safe. "percpu", for example, requires special // unwrapping with `bpf_per_cpu_ptr` which is not yet supported. static const std::string_view allowed_tag = "rcu"; for (const auto &tag : tags) { if (tag != allowed_tag) { LOG(ERROR, acc.loc, err_) << "Attempting to access pointer field '" << acc.field << "' with unsupported tag attribute: " << tag; } } } // Only set acc.type if it's not already set. See explanation [1]. if (acc.type.IsNoneTy()) acc.type = field.type; if (acc.expr->type.IsCtxAccess() && (acc.type.IsArrayTy() || acc.type.IsRecordTy())) { // e.g., ((struct bpf_perf_event_data*)ctx)->regs.ax acc.type.MarkCtxAccess(); } acc.type.is_internal = type.is_internal; acc.type.is_btftype = type.is_btftype; acc.type.SetAS(acc.expr->type.GetAS()); // The kernel uses the first 8 bytes to store `struct pt_regs`. Any // access to the first 8 bytes results in verifier error. if (type.is_tparg && field.offset < 8) LOG(ERROR, acc.loc, err_) << "BPF does not support accessing common tracepoint fields"; } } } // We can't hint for unsigned types. It is a syntax error, // because the word "unsigned" is not allowed in a type name. static std::unordered_map KNOWN_TYPE_ALIASES{ { "char", "int8" }, /* { "unsigned char", "uint8" }, */ { "short", "int16" }, /* { "unsigned short", "uint16" }, */ { "int", "int32" }, /* { "unsigned int", "uint32" }, */ { "long", "int64" }, /* { "unsigned long", "uint64" }, */ }; void SemanticAnalyser::visit(Cast &cast) { cast.expr = dereference_if_needed(cast.expr); // cast type is synthesised in parser, if it is a struct, it needs resolving resolve_struct_type(cast.type, cast.loc); auto rhs = cast.expr->type; if (rhs.IsRecordTy()) { LOG(ERROR, cast.loc, err_) << "Cannot cast from struct type \"" << cast.expr->type << "\""; } else if (rhs.IsNoneTy()) { LOG(ERROR, cast.loc, err_) << "Cannot cast from \"" << cast.expr->type << "\" type"; } if (!cast.type.IsIntTy() && !cast.type.IsPtrTy() && !(cast.type.IsPtrTy() && !cast.type.GetElementTy()->IsIntTy() && !cast.type.GetElementTy()->IsRecordTy()) && // we support casting integers to int arrays !(cast.type.IsArrayTy() && cast.type.GetElementTy()->IsIntTy())) { LOG(ERROR, cast.loc, err_) << "Cannot cast to \"" << cast.type << "\""; if (auto it = KNOWN_TYPE_ALIASES.find(cast.type.GetName()); it != KNOWN_TYPE_ALIASES.end()) { LOG(HINT, cast.loc, err_) << "Did you mean \"" << it->second << "\"?"; } } if (cast.type.IsArrayTy()) { if (cast.type.GetElementTy()->IsBoolTy()) { LOG(ERROR, cast.loc, err_) << "Bit arrays are not supported"; return; } if (cast.type.GetNumElements() == 0) { if (cast.type.GetElementTy()->GetSize() == 0) LOG(ERROR, cast.loc, err_) << "Could not determine size of the array"; else { if (rhs.GetSize() % cast.type.GetElementTy()->GetSize() != 0) { LOG(ERROR, cast.loc, err_) << "Cannot determine array size: the element size is " "incompatible with the cast integer size"; } // cast to unsized array (e.g. int8[]), determine size from RHS auto num_elems = rhs.GetSize() / cast.type.GetElementTy()->GetSize(); cast.type = CreateArray(num_elems, *cast.type.GetElementTy()); } } if (rhs.IsIntTy()) cast.type.is_internal = true; } if (cast.type.IsEnumTy()) { if (bpftrace_.enum_defs_.count(cast.type.GetName()) == 0) { LOG(ERROR, cast.loc, err_) << "Unknown enum: " << cast.type.GetName(); } else { if (rhs.IsIntTy() && cast.expr->is_literal) { auto integer = static_cast(cast.expr); if (bpftrace_.enum_defs_[cast.type.GetName()].count(integer->n) == 0) { LOG(ERROR, cast.expr->loc, err_) << "Enum: " << cast.type.GetName() << " doesn't contain a variant value of " << integer->n; } } } } if ((cast.type.IsIntTy() && !rhs.IsIntTy() && !rhs.IsPtrTy() && !rhs.IsCtxAccess() && !rhs.IsArrayTy() && !rhs.IsCastableMapTy()) || // casting from/to int arrays must respect the size (cast.type.IsArrayTy() && (!rhs.IsIntTy() || cast.type.GetSize() != rhs.GetSize())) || (rhs.IsArrayTy() && (!cast.type.IsIntTy() || cast.type.GetSize() != rhs.GetSize()))) { LOG(ERROR, cast.loc, err_) << "Cannot cast from \"" << rhs << "\" to \"" << cast.type << "\""; } if (cast.expr->type.IsCtxAccess() && !cast.type.IsIntTy()) cast.type.MarkCtxAccess(); cast.type.SetAS(cast.expr->type.GetAS()); // case : BEGIN { @foo = (struct Foo)0; } // case : profile:hz:99 $task = (struct task_struct *)curtask. if (cast.type.GetAS() == AddrSpace::none) { if (auto probe = dynamic_cast(top_level_node_)) { ProbeType type = single_provider_type(probe); cast.type.SetAS(find_addrspace(type)); } else { // Assume kernel space for data in subprogs cast.type.SetAS(AddrSpace::kernel); } } } void SemanticAnalyser::visit(Tuple &tuple) { std::vector elements; for (auto &elem : tuple.elems) { elem = dereference_if_needed(elem); // If elem type is none that means that the tuple contains some // invalid cast (e.g., (0, (aaa)0)). In this case, skip the tuple // creation. Cast already emits the error. if (elem->type.IsNoneTy() || elem->type.GetSize() == 0) { return; } else if (elem->type.IsMultiOutputMapTy()) { LOG(ERROR, elem->loc, err_) << "Map type " << elem->type << " cannot exist inside a tuple."; } elements.emplace_back(elem->type); } tuple.type = CreateTuple(bpftrace_.structs.AddTuple(elements)); } void SemanticAnalyser::visit(ExprStatement &expr) { expr.expr = dereference_if_needed(expr.expr); } static const std::unordered_map AGGREGATE_HINTS{ { Type::count_t, "count()" }, { Type::sum_t, "sum(retval)" }, { Type::min_t, "min(retval)" }, { Type::max_t, "max(retval)" }, { Type::avg_t, "avg(retval)" }, { Type::hist_t, "hist(retval)" }, { Type::lhist_t, "lhist(rand %10, 0, 10, 1)" }, { Type::stats_t, "stats(arg2)" }, }; void SemanticAnalyser::visit(AssignMapStatement &assignment) { assignment.map->is_read = false; visit(assignment.map); assignment.expr = dereference_if_needed(assignment.expr); const auto *map_type_before = get_map_type(*assignment.map); if (!is_valid_assignment(assignment.map, assignment.expr)) { const auto &type = assignment.expr->type; auto hint = AGGREGATE_HINTS.find(type.GetTy()); if (hint == AGGREGATE_HINTS.end()) LOG(BUG) << "Missing assignment hint"; LOG(ERROR, assignment.loc, err_) << "Map value '" << type << "' cannot be assigned from one map to another. " "The function that returns this type must be called directly e.g. `" << assignment.map->ident << " = " << hint->second << ";`."; if (auto *expr_map = dynamic_cast(assignment.expr)) { if (type.IsCastableMapTy()) { LOG(HINT, err_) << "Add a cast to integer if you want the value of the aggregate, " "e.g. `" << assignment.map->ident << " = (int64)" << expr_map->ident << ";`."; } } } // Add an implicit cast when copying the value of an aggregate map to an // existing map of int. Enables the following: `@x = 1; @y = count(); @x = @y` const bool map_contains_int = map_type_before && map_type_before->IsIntTy(); const bool expr_is_map_with_castable_agg = assignment.expr->is_map && assignment.expr->type.IsCastableMapTy(); if (map_contains_int && expr_is_map_with_castable_agg) { assignment.expr = ctx_.make_node(*map_type_before, assignment.expr, assignment.expr->loc); } assign_map_type(*assignment.map, assignment.expr->type); const auto &map_ident = assignment.map->ident; const auto &type = assignment.expr->type; if (type.IsRecordTy() && map_val_[map_ident].IsRecordTy()) { std::string ty = assignment.expr->type.GetName(); std::string stored_ty = map_val_[map_ident].GetName(); if (!stored_ty.empty() && stored_ty != ty) { LOG(ERROR, assignment.loc, err_) << "Type mismatch for " << map_ident << ": " << "trying to assign value of type '" << ty << "' when map already contains a value of type '" << stored_ty << "'"; } else { map_val_[map_ident] = assignment.expr->type; map_val_[map_ident].is_internal = true; } } else if (type.IsStringTy()) { auto map_size = map_val_[map_ident].GetSize(); auto expr_size = assignment.expr->type.GetSize(); if (map_size < expr_size) { LOG(WARNING, assignment.loc, out_) << "String size mismatch: " << map_size << " < " << expr_size << ". The value may be truncated."; } } else if (type.IsBufferTy()) { auto map_size = map_val_[map_ident].GetSize(); auto expr_size = assignment.expr->type.GetSize(); if (map_size != expr_size) { std::stringstream buf; buf << "Buffer 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(); } } } else if (type.IsCtxAccess()) { // bpf_map_update_elem() only accepts a pointer to a element in the stack LOG(ERROR, assignment.loc, err_) << "context cannot be assigned to a map"; } else if (type.IsTupleTy()) { // Early passes may not have been able to deduce the full types of tuple // elements yet. So wait until final pass. if (is_final_pass()) { const auto &map_type = map_val_[map_ident]; const auto &expr_type = assignment.expr->type; if (!expr_type.FitsInto(map_type)) { LOG(ERROR, assignment.loc, err_) << "Tuple type mismatch: " << map_type << " != " << expr_type << "."; } } } else if (type.IsArrayTy()) { const auto &map_type = map_val_[map_ident]; const auto &expr_type = assignment.expr->type; if (map_type == expr_type) { map_val_[map_ident].is_internal = true; } else { LOG(ERROR, assignment.loc, err_) << "Array type mismatch: " << map_type << " != " << expr_type << "."; } } } void SemanticAnalyser::visit(AssignVarStatement &assignment) { assignment.expr = dereference_if_needed(assignment.expr); if (assignment.var_decl_stmt) { visit(assignment.var_decl_stmt); } if (!is_valid_assignment(assignment.var, assignment.expr)) { LOG(ERROR, assignment.loc, err_) << "Map value '" << assignment.expr->type << "' cannot be assigned to a scratch variable."; } const bool expr_is_map_with_castable_agg = assignment.expr->is_map && assignment.expr->type.IsCastableMapTy(); if (expr_is_map_with_castable_agg) { assignment.expr = ctx_.make_node(CreateInt64(), assignment.expr, assignment.expr->loc); } Node *var_scope = nullptr; const auto &var_ident = assignment.var->ident; const auto &assignTy = assignment.expr->type; if (auto *scope = find_variable_scope(var_ident)) { auto &foundVar = variables_[scope][var_ident]; auto &storedTy = foundVar.type; bool type_mismatch_error = false; if (storedTy.IsNoneTy()) { storedTy = assignTy; } else if (!storedTy.IsSameType(assignTy)) { if (!assignTy.IsNoneTy() || is_final_pass()) { type_mismatch_error = true; } else { pass_tracker_.inc_num_unresolved(); } } else if (assignTy.IsStringTy()) { if (foundVar.can_resize) { update_string_size(storedTy, assignTy); } else if (!assignTy.FitsInto(storedTy)) { type_mismatch_error = true; } } else if (storedTy.IsIntegerTy()) { if (assignment.expr->is_literal) { auto integer = static_cast(assignment.expr); if (!storedTy.IsEqual(assignTy)) { int64_t value = integer->n; bool can_fit = false; if (integer->is_negative) { if (!storedTy.IsSigned()) { type_mismatch_error = true; } else { auto min_max = getIntTypeRange(storedTy); can_fit = value >= min_max.first; } } else { if (!storedTy.IsSigned()) { auto min_max = getUIntTypeRange(storedTy); can_fit = static_cast(value) <= min_max.second; } else { // Casting to a uint64 here because the assign 'value' // might be larger than the max signed int64 e.g. // `$x = -1; $x = 10223372036854775807;` auto min_max = getIntTypeRange(storedTy); can_fit = static_cast(value) <= static_cast(min_max.second); } } if (can_fit) { Expression *cast = ctx_.make_node( CreateInteger(storedTy.GetSize() * 8, storedTy.IsSigned()), assignment.expr, assignment.loc); assignment.expr = dereference_if_needed(cast); } else if (!type_mismatch_error) { LOG(ERROR, assignment.loc, err_) << "Type mismatch for " << var_ident << ": " << "trying to assign value '" << (integer->is_negative ? integer->n : static_cast(integer->n)) << "' which does not fit into the variable of type '" << storedTy << "'"; } } } else if (storedTy.IsSigned() != assignTy.IsSigned()) { type_mismatch_error = true; } else { if (!assignTy.FitsInto(storedTy)) { LOG(ERROR, assignment.loc, err_) << "Integer size mismatch. Assignment type '" << assignTy << "' is larger than the variable type '" << storedTy << "'."; } } } else if (assignTy.IsBufferTy()) { auto var_size = storedTy.GetSize(); auto expr_size = assignTy.GetSize(); if (var_size != expr_size) { LOG(WARNING, assignment.loc, out_) << "Buffer size mismatch: " << var_size << " != " << expr_size << (var_size < expr_size ? ". The value may be truncated." : ". The value may contain garbage."); } } else if (assignTy.IsTupleTy()) { update_string_size(storedTy, assignTy); // Early passes may not have been able to deduce the full types of tuple // elements yet. So wait until final pass. if (is_final_pass()) { if (!assignTy.FitsInto(storedTy)) { type_mismatch_error = true; } } } if (type_mismatch_error) { auto err_segment = foundVar.was_assigned ? "when variable already contains a value of type" : "when variable already has a type"; LOG(ERROR, assignment.loc, err_) << "Type mismatch for " << var_ident << ": " << "trying to assign value of type '" << assignTy << "' " << err_segment << " '" << storedTy << "'"; } else { if (!foundVar.was_assigned) { // The assign type is possibly more complete than the stored type, // which could come from a variable declaration. The assign type may // resolve builtins like `curtask` which also specifies the address // space. foundVar.type = assignTy; foundVar.was_assigned = true; } var_scope = scope; } } if (var_scope == nullptr) { variables_[scope_stack_.back()].insert( { var_ident, { .type = assignTy, .can_resize = true, .was_assigned = true } }); var_scope = scope_stack_.back(); } const auto &storedTy = variables_[var_scope][var_ident].type; assignment.var->type = storedTy; if (is_final_pass()) { if (storedTy.IsNoneTy()) LOG(ERROR, assignment.expr->loc, err_) << "Invalid expression for assignment: " << storedTy; } } void SemanticAnalyser::visit(AssignConfigVarStatement &assignment) { assignment.expr = dereference_if_needed(assignment.expr); } void SemanticAnalyser::visit(VarDeclStatement &decl) { const std::string &var_ident = decl.var->ident; if (!IsValidVarDeclType(decl.var->type)) { LOG(ERROR, decl.loc, err_) << "Invalid variable declaration type: " << decl.var->type; } // Only checking on the first pass for cases like this: // `BEGIN { if (1) { let $x; } else { let $x; } let $x; }` // Notice how the last `let $x` is defined in the outer scope; // this means on subsequent passes the first two `let $x` statements // would be considered variable shadowing, when in fact, because of order, // there is no ambiguity in terms of future assignment and use. if (is_first_pass()) { for (auto scope : scope_stack_) { // This should be the first time we're seeing this variable if (auto decl_search = variable_decls_[scope].find(var_ident); decl_search != variable_decls_[scope].end()) { if (decl_search->second != decl.loc) { LOG(ERROR, decl.loc, err_) << "Variable " << var_ident << " was already declared. Variable shadowing is not allowed."; LOG(ERROR, decl_search->second, err_) << "Initial declaration"; } } } } if (is_first_pass() || is_final_pass()) { if (auto *scope = find_variable_scope(var_ident)) { auto &foundVar = variables_[scope][var_ident]; // Checking the first pass only for cases like this: // `BEGIN { if (1) { let $x; } $x = 2; }` // Again, this is legal and there is no ambiguity but `$x = 2` gets // placed in the outer scope so subsequent passes would consider // this a use before declaration error (below) if (!variable_decls_[scope].contains(var_ident) && is_first_pass()) { LOG(ERROR, decl.loc, err_) << "Variable declarations need to occur before variable usage or " "assignment. Variable: " << var_ident; } else if (is_final_pass()) { // Update the declaration type if it was either not set e.g. `let $a;` // or the type is ambiguous or resizable e.g. `let $a: string;` decl.var->type = foundVar.type; } if (is_final_pass() && !foundVar.was_assigned) { LOG(WARNING, decl.loc, out_) << "Variable " << var_ident << " never assigned to."; } return; } } bool can_resize = !decl.set_type || decl.var->type.GetSize() == 0; variables_[scope_stack_.back()].insert({ var_ident, { .type = decl.var->type, .can_resize = can_resize, .was_assigned = false } }); variable_decls_[scope_stack_.back()].insert({ var_ident, decl.loc }); } void SemanticAnalyser::visit(Predicate &pred) { pred.expr = dereference_if_needed(pred.expr); if (is_final_pass()) { SizedType &ty = pred.expr->type; if (!ty.IsIntTy() && !ty.IsPtrTy()) { LOG(ERROR, pred.loc, err_) << "Invalid type for predicate: " << pred.expr->type.GetTy(); } } } void SemanticAnalyser::visit(AttachPoint &ap) { if (ap.provider == "kprobe" || ap.provider == "kretprobe") { if (ap.func == "") LOG(ERROR, ap.loc, err_) << "kprobes should be attached to a function"; if (is_final_pass()) { // Warn if user tries to attach to a non-traceable function if (bpftrace_.config_.get(ConfigKeyMissingProbes::default_) != ConfigMissingProbes::ignore && !has_wildcard(ap.func) && !bpftrace_.is_traceable_func(ap.func)) { LOG(WARNING, ap.loc, out_) << ap.func << " is not traceable (either non-existing, inlined, or marked as " "\"notrace\"); attaching to it will likely fail"; } } } else if (ap.provider == "uprobe" || ap.provider == "uretprobe") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << ap.provider << " should have a target"; if (ap.func == "" && ap.address == 0) LOG(ERROR, ap.loc, err_) << ap.provider << " should be attached to a function and/or address"; if (ap.lang != "" && !is_supported_lang(ap.lang)) LOG(ERROR, ap.loc, err_) << "unsupported language type: " << ap.lang; if (ap.provider == "uretprobe" && ap.func_offset != 0) LOG(ERROR, ap.loc, err_) << "uretprobes can not be attached to a function offset"; std::vector paths; if (ap.target == "*") { if (bpftrace_.pid() > 0) paths = get_mapped_paths_for_pid(bpftrace_.pid()); else paths = get_mapped_paths_for_running_pids(); } else { paths = resolve_binary_path(ap.target, bpftrace_.pid()); } switch (paths.size()) { case 0: LOG(ERROR, ap.loc, err_) << "uprobe target file '" << ap.target << "' does not exist or is not executable"; break; case 1: // Replace the glob at this stage only if this is *not* a wildcard, // otherwise we rely on the probe matcher. This is not going through // any interfaces that can be properly mocked. if (ap.target.find("*") == std::string::npos) ap.target = paths.front(); break; default: // If we are doing a PATH lookup (ie not glob), we follow shell // behavior and take the first match. // Otherwise we keep the target with glob, it will be expanded later if (ap.target.find("*") == std::string::npos) { LOG(WARNING, ap.loc, out_) << "attaching to uprobe target file '" << paths.front() << "' but matched " << std::to_string(paths.size()) << " binaries"; ap.target = paths.front(); } } } else if (ap.provider == "usdt") { bpftrace_.has_usdt_ = true; if (ap.func == "") LOG(ERROR, ap.loc, err_) << "usdt probe must have a target function or wildcard"; if (ap.target != "" && !(bpftrace_.pid() > 0 && has_wildcard(ap.target))) { auto paths = resolve_binary_path(ap.target, bpftrace_.pid()); switch (paths.size()) { case 0: LOG(ERROR, ap.loc, err_) << "usdt target file '" << ap.target << "' does not exist or is not executable"; break; case 1: // See uprobe, above. if (ap.target.find("*") == std::string::npos) ap.target = paths.front(); break; default: // See uprobe, above. if (ap.target.find("*") == std::string::npos) { LOG(WARNING, ap.loc, out_) << "attaching to usdt target file '" << paths.front() << "' but matched " << std::to_string(paths.size()) << " binaries"; ap.target = paths.front(); } } } if (bpftrace_.pid() > 0) { USDTHelper::probes_for_pid(bpftrace_.pid()); } else if (ap.target == "*") { USDTHelper::probes_for_all_pids(); } else if (ap.target != "") { for (auto &path : resolve_binary_path(ap.target)) USDTHelper::probes_for_path(path); } else { LOG(ERROR, ap.loc, err_) << "usdt probe must specify at least path or pid to probe. To target " "all paths/pids set the path to '*'."; } } else if (ap.provider == "tracepoint") { if (ap.target == "" || ap.func == "") LOG(ERROR, ap.loc, err_) << "tracepoint probe must have a target"; } else if (ap.provider == "rawtracepoint") { if (ap.target != "") LOG(ERROR, ap.loc, err_) << "rawtracepoint should not have a target"; if (ap.func == "") LOG(ERROR, ap.loc, err_) << "rawtracepoint should be attached to a function"; } else if (ap.provider == "profile") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << "profile probe must have unit of time"; else if (!listing_) { if (TIME_UNITS.find(ap.target) == TIME_UNITS.end()) LOG(ERROR, ap.loc, err_) << ap.target << " is not an accepted unit of time"; if (ap.func != "") LOG(ERROR, ap.loc, err_) << "profile probe must have an integer frequency"; else if (ap.freq <= 0) LOG(ERROR, ap.loc, err_) << "profile frequency should be a positive integer"; } } else if (ap.provider == "interval") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << "interval probe must have unit of time"; else if (!listing_) { if (TIME_UNITS.find(ap.target) == TIME_UNITS.end()) LOG(ERROR, ap.loc, err_) << ap.target << " is not an accepted unit of time"; if (ap.func != "") LOG(ERROR, ap.loc, err_) << "interval probe must have an integer frequency"; else if (ap.freq <= 0) LOG(ERROR, ap.loc, err_) << "interval frequency should be a positive integer"; } } else if (ap.provider == "software") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << "software probe must have a software event name"; else { if (!has_wildcard(ap.target) && !ap.ignore_invalid) { bool found = false; for (auto &probeListItem : SW_PROBE_LIST) { if (ap.target == probeListItem.path || (!probeListItem.alias.empty() && ap.target == probeListItem.alias)) { found = true; break; } } if (!found) LOG(ERROR, ap.loc, err_) << ap.target << " is not a software probe"; } else if (!listing_) { LOG(ERROR, ap.loc, err_) << "wildcards are not allowed for hardware probe type"; } } if (ap.func != "") LOG(ERROR, ap.loc, err_) << "software probe can only have an integer count"; else if (ap.freq < 0) LOG(ERROR, ap.loc, err_) << "software count should be a positive integer"; } else if (ap.provider == "watchpoint" || ap.provider == "asyncwatchpoint") { if (ap.func.size()) { if (bpftrace_.pid() <= 0 && !has_child_) LOG(ERROR, ap.loc, err_) << "-p PID or -c CMD required for watchpoint"; if (ap.address > static_cast(arch::max_arg())) LOG(ERROR, ap.loc, err_) << arch::name() << " doesn't support arg" << ap.address; } else if (ap.provider == "asyncwatchpoint") LOG(ERROR, ap.loc, err_) << ap.provider << " requires a function name"; else if (!ap.address) LOG(ERROR, ap.loc, err_) << "watchpoint must be attached to a non-zero address"; if (ap.len != 1 && ap.len != 2 && ap.len != 4 && ap.len != 8) LOG(ERROR, ap.loc, err_) << "watchpoint length must be one of (1,2,4,8)"; if (ap.mode.empty()) LOG(ERROR, ap.loc, err_) << "watchpoint mode must be combination of (r,w,x)"; std::sort(ap.mode.begin(), ap.mode.end()); for (const char c : ap.mode) { if (c != 'r' && c != 'w' && c != 'x') LOG(ERROR, ap.loc, err_) << "watchpoint mode must be combination of (r,w,x)"; } for (size_t i = 1; i < ap.mode.size(); ++i) { if (ap.mode[i - 1] == ap.mode[i]) LOG(ERROR, ap.loc, err_) << "watchpoint modes may not be duplicated"; } const auto invalid_modes = arch::invalid_watchpoint_modes(); if (std::any_of(invalid_modes.cbegin(), invalid_modes.cend(), [&](const auto &mode) { return mode == ap.mode; })) LOG(ERROR, ap.loc, err_) << "invalid watchpoint mode: " << ap.mode; } else if (ap.provider == "hardware") { if (ap.target == "") LOG(ERROR, ap.loc, err_) << "hardware probe must have a hardware event name"; else { if (!has_wildcard(ap.target) && !ap.ignore_invalid) { bool found = false; for (auto &probeListItem : HW_PROBE_LIST) { if (ap.target == probeListItem.path || (!probeListItem.alias.empty() && ap.target == probeListItem.alias)) { found = true; break; } } if (!found) LOG(ERROR, ap.loc, err_) << ap.target + " is not a hardware probe"; } else if (!listing_) { LOG(ERROR, ap.loc, err_) << "wildcards are not allowed for hardware probe type"; } } if (ap.func != "") LOG(ERROR, ap.loc, err_) << "hardware probe can only have an integer count"; else if (ap.freq < 0) LOG(ERROR, ap.loc, err_) << "hardware frequency should be a positive integer"; } else if (ap.provider == "BEGIN" || ap.provider == "END") { if (ap.target != "" || ap.func != "") LOG(ERROR, ap.loc, err_) << "BEGIN/END probes should not have a target"; if (is_final_pass()) { if (ap.provider == "BEGIN") { if (has_begin_probe_) LOG(ERROR, ap.loc, err_) << "More than one BEGIN probe defined"; has_begin_probe_ = true; } if (ap.provider == "END") { if (has_end_probe_) LOG(ERROR, ap.loc, err_) << "More than one END probe defined"; has_end_probe_ = true; } } } else if (ap.provider == "self") { if (ap.target == "signal") { if (SIGNALS.find(ap.func) == SIGNALS.end()) LOG(ERROR, ap.loc, err_) << ap.func << " is not a supported signal"; return; } LOG(ERROR, ap.loc, err_) << ap.target << " is not a supported trigger"; } else if (ap.provider == "fentry" || ap.provider == "fexit") { if (!bpftrace_.feature_->has_fentry()) { LOG(ERROR, ap.loc, err_) << "fentry/fexit not available for your kernel version."; return; } if (ap.func == "") LOG(ERROR, ap.loc, err_) << "fentry/fexit should specify a function"; } else if (ap.provider == "iter") { if (!listing_ && bpftrace_.btf_->get_all_iters().count(ap.func) <= 0) { LOG(ERROR, ap.loc, err_) << "iter " << ap.func << " not available for your kernel version."; } if (ap.func == "") LOG(ERROR, ap.loc, err_) << "iter should specify a iterator's name"; } else { LOG(ERROR, ap.loc, err_) << "Invalid provider: '" << ap.provider << "'"; } } void SemanticAnalyser::visit(Block &block) { scope_stack_.push_back(&block); accept_statements(block.stmts); scope_stack_.pop_back(); } void SemanticAnalyser::visit(Probe &probe) { auto aps = probe.attach_points.size(); top_level_node_ = &probe; for (AttachPoint *ap : probe.attach_points) { if (!listing_ && aps > 1 && ap->provider == "iter") { LOG(ERROR, ap->loc, err_) << "Only single iter attach point is allowed."; return; } visit(ap); } visit(probe.pred); visit(probe.block); } void SemanticAnalyser::visit(Config &config) { accept_statements(config.stmts); } void SemanticAnalyser::visit(Subprog &subprog) { scope_stack_.push_back(&subprog); top_level_node_ = &subprog; for (SubprogArg *arg : subprog.args) { variables_[scope_stack_.back()].insert( { arg->name(), { .type = arg->type, .can_resize = true, .was_assigned = true } }); } Visitor::visit(subprog); scope_stack_.pop_back(); } int SemanticAnalyser::analyse() { std::string errors; int last_num_unresolved = 0; // Multiple passes to handle variables being used before they are defined while (true) { pass_tracker_.reset_num_unresolved(); visit(ctx_.root); errors = err_.str(); if (!errors.empty()) { out_ << errors; return pass_tracker_.get_num_passes(); } if (is_final_pass()) { return 0; } int num_unresolved = pass_tracker_.get_num_unresolved(); if (num_unresolved > 0 && (last_num_unresolved == 0 || num_unresolved < last_num_unresolved)) { // If we're making progress, keep making passes last_num_unresolved = num_unresolved; } else { pass_tracker_.mark_final_pass(); } pass_tracker_.inc_num_passes(); } } inline bool SemanticAnalyser::is_final_pass() const { return pass_tracker_.is_final_pass(); } bool SemanticAnalyser::is_first_pass() const { return pass_tracker_.get_num_passes() == 1; } bool SemanticAnalyser::check_assignment(const Call &call, bool want_map, bool want_var, bool want_map_key) { if (want_map && want_var && want_map_key) { if (!call.map && !call.var && !call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a map or a " "variable, or be used as a map key"; return false; } } else if (want_map && want_var) { if (!call.map && !call.var) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a map or a variable"; return false; } } else if (want_map && want_map_key) { if (!call.map && !call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a map or be used as a map key"; return false; } } else if (want_var && want_map_key) { if (!call.var && !call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a variable or be used as a map key"; return false; } } else if (want_map) { if (!call.map) { LOG(ERROR, call.loc, err_) << call.func << "() should be directly assigned to a map"; return false; } } else if (want_var) { if (!call.var) { LOG(ERROR, call.loc, err_) << call.func << "() should be assigned to a variable"; return false; } } else if (want_map_key) { if (!call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should be used as a map key"; return false; } } else { if (call.map || call.var || call.key_for_map) { LOG(ERROR, call.loc, err_) << call.func << "() should not be used in an assignment or as a map key"; return false; } } return true; } // Checks the number of arguments passed to a function is correct. bool SemanticAnalyser::check_nargs(const Call &call, size_t expected_nargs) { std::stringstream err; auto nargs = call.vargs.size(); if (nargs != expected_nargs) { if (expected_nargs == 0) err << call.func << "() requires no arguments"; else if (expected_nargs == 1) err << call.func << "() requires one argument"; else err << call.func << "() requires " << expected_nargs << " arguments"; err << " (" << nargs << " provided)"; LOG(ERROR, call.loc, err_) << err.str(); return false; } return true; } // Checks the number of arguments passed to a function is within a specified // range. bool SemanticAnalyser::check_varargs(const Call &call, size_t min_nargs, size_t max_nargs) { std::stringstream err; auto nargs = call.vargs.size(); if (nargs < min_nargs) { if (min_nargs == 1) err << call.func << "() requires at least one argument"; else err << call.func << "() requires at least " << min_nargs << " arguments"; err << " (" << nargs << " provided)"; LOG(ERROR, call.loc, err_) << err.str(); return false; } else if (nargs > max_nargs) { if (max_nargs == 0) err << call.func << "() requires no arguments"; else if (max_nargs == 1) err << call.func << "() takes up to one argument"; else err << call.func << "() takes up to " << max_nargs << " arguments"; err << " (" << nargs << " provided)"; LOG(ERROR, call.loc, err_) << err.str(); return false; } return true; } // Checks an argument passed to a function is of the correct type. // // This function does not check that the function has the correct number of // arguments. Either check_nargs() or check_varargs() should be called first to // validate this. bool SemanticAnalyser::check_arg(const Call &call, Type type, int arg_num, bool want_literal, bool fail) { auto &arg = *call.vargs.at(arg_num); if (want_literal && (!arg.is_literal || arg.type.GetTy() != type)) { if (fail) { LOG(ERROR, call.loc, err_) << call.func << "() expects a " << type << " literal (" << arg.type.GetTy() << " provided)"; if (type == Type::string) { // If the call requires a string literal and a positional parameter is // given, tell user to use str() auto *pos_param = dynamic_cast(&arg); if (pos_param) LOG(ERROR) << "Use str($" << pos_param->n << ") to treat $" << pos_param->n << " as a string"; } } return false; } else if (is_final_pass() && arg.type.GetTy() != type) { if (fail) { LOG(ERROR, call.loc, err_) << call.func << "() only supports " << type << " arguments (" << arg.type.GetTy() << " provided)"; } return false; } return true; } bool SemanticAnalyser::check_symbol(const Call &call, int arg_num __attribute__((unused))) { auto arg = bpftrace_.get_string_literal(call.vargs.at(0)); std::string re = "^[a-zA-Z0-9./_-]+$"; bool is_valid = std::regex_match(arg, std::regex(re)); if (!is_valid) { LOG(ERROR, call.loc, err_) << call.func << "() expects a string that is a valid symbol (" << re << ") as input (\"" << arg << "\" provided)"; return false; } return true; } bool SemanticAnalyser::check_available(const Call &call, const AttachPoint &ap) { auto &func = call.func; ProbeType type = probetype(ap.provider); if (func == "reg") { switch (type) { case ProbeType::kprobe: case ProbeType::kretprobe: case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: case ProbeType::profile: case ProbeType::interval: case ProbeType::software: case ProbeType::hardware: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: return true; case ProbeType::invalid: case ProbeType::special: case ProbeType::tracepoint: case ProbeType::fentry: case ProbeType::fexit: case ProbeType::iter: case ProbeType::rawtracepoint: return false; } } else if (func == "uaddr") { switch (type) { case ProbeType::usdt: case ProbeType::uretprobe: case ProbeType::uprobe: return true; case ProbeType::invalid: case ProbeType::special: case ProbeType::kprobe: case ProbeType::kretprobe: case ProbeType::tracepoint: case ProbeType::profile: case ProbeType::interval: case ProbeType::software: case ProbeType::hardware: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::fentry: case ProbeType::fexit: case ProbeType::iter: case ProbeType::rawtracepoint: return false; } } else if (func == "signal") { switch (type) { case ProbeType::kprobe: case ProbeType::kretprobe: case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: case ProbeType::tracepoint: case ProbeType::profile: case ProbeType::fentry: case ProbeType::fexit: case ProbeType::rawtracepoint: return true; case ProbeType::invalid: case ProbeType::special: case ProbeType::interval: case ProbeType::software: case ProbeType::hardware: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::iter: return false; } } if (type == ProbeType::invalid) return false; return true; } SizedType *SemanticAnalyser::get_map_type(const Map &map) { const std::string &map_ident = map.ident; auto search = map_val_.find(map_ident); if (search == map_val_.end()) return nullptr; return &search->second; } SizedType *SemanticAnalyser::get_map_key_type(const Map &map) { if (auto it = map_key_.find(map.ident); it != map_key_.end()) { return &it->second; } return nullptr; } // assign_map_type // // Semantic analysis for assigning a value of the provided type // to the given map. void SemanticAnalyser::assign_map_type(const Map &map, const SizedType &type) { const std::string &map_ident = map.ident; if (type.IsRecordTy() && type.is_tparg) { LOG(ERROR, map.loc, err_) << "Storing tracepoint args in maps is not supported"; } auto *maptype = get_map_type(map); if (maptype) { if (maptype->IsNoneTy()) { pass_tracker_.inc_num_unresolved(); if (is_final_pass()) LOG(ERROR, map.loc, err_) << "Undefined map: " + map_ident; else *maptype = type; } else if (maptype->GetTy() != type.GetTy()) { LOG(ERROR, map.loc, err_) << "Type mismatch for " << map_ident << ": " << "trying to assign value of type '" << type << "' when map already contains a value of type '" << *maptype << "'"; } if (maptype->IsStringTy() || maptype->IsTupleTy()) update_string_size(*maptype, type); } else { // This map hasn't been seen before map_val_.insert({ map_ident, type }); if (map_val_[map_ident].IsIntTy()) { // Store all integer values as 64-bit in maps, so that there will // be space for any integer to be assigned to the map later map_val_[map_ident].SetSize(8); } } } void SemanticAnalyser::accept_statements(StatementList &stmts) { for (size_t i = 0; i < stmts.size(); i++) { visit(stmts.at(i)); auto stmt = stmts.at(i); if (is_final_pass()) { auto *jump = dynamic_cast(stmt); if (jump && i < (stmts.size() - 1)) { LOG(WARNING, jump->loc, out_) << "All code after a '" << opstr(*jump) << "' is unreachable."; } } } } SizedType SemanticAnalyser::create_key_type(const SizedType &expr_type, const location &loc) { SizedType new_key_type = expr_type; if (expr_type.IsTupleTy()) { std::vector elements; for (const auto &field : expr_type.GetFields()) { SizedType keytype = create_key_type(field.type, loc); elements.push_back(std::move(keytype)); } new_key_type = CreateTuple(bpftrace_.structs.AddTuple(elements)); } else if (expr_type.IsIntegerTy()) { // Store all integer values as 64-bit in map keys, so that there will // be space for any integer in the map key later // This should have a better solution. new_key_type.SetSign(true); new_key_type.SetIntBitWidth(64); } validate_map_key(new_key_type, loc); return new_key_type; } void SemanticAnalyser::update_current_key(SizedType ¤t_key_type, const SizedType &new_key_type) { if (current_key_type.IsSameType(new_key_type) && (current_key_type.IsStringTy() || current_key_type.IsTupleTy())) { update_string_size(current_key_type, new_key_type); } } void SemanticAnalyser::validate_new_key(const SizedType ¤t_key_type, const SizedType &new_key_type, const std::string &map_ident, const location &loc) { // Map keys can get resized/updated across multiple passes // wait till the end to log an error if there is a key mismatch. if (!is_final_pass()) { return; } bool valid = true; if (current_key_type.IsSameType(new_key_type)) { if (current_key_type.IsTupleTy() || current_key_type.IsIntegerTy() || current_key_type.IsStringTy()) { // This should always be true as map integer keys default to 64 bits // and strings get resized (this happens recursively into tuples as well) // but keep this here just in case we add larger ints and need to // update the map int logic if (!new_key_type.FitsInto(current_key_type)) { valid = false; } } else if (!current_key_type.IsEqual(new_key_type)) { valid = false; } } else { valid = false; } if (valid) { return; } if (current_key_type.IsNoneTy()) { LOG(ERROR, loc, err_) << "Argument mismatch for " << map_ident << ": " << "trying to access with arguments: '" << new_key_type << "' when map expects no arguments"; } else { LOG(ERROR, loc, err_) << "Argument mismatch for " << map_ident << ": " << "trying to access with arguments: '" << new_key_type << "' when map expects arguments: '" << current_key_type << "'"; } } 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; } SizedType SemanticAnalyser::create_merged_tuple(const SizedType &left, const SizedType &right) { assert(left.IsTupleTy() && right.IsTupleTy() && (left.GetFieldCount() == right.GetFieldCount())); std::vector new_elems; for (ssize_t i = 0; i < left.GetFieldCount(); i++) { const auto &leftTy = left.GetField(i).type; const auto &rightTy = right.GetField(i).type; assert(leftTy.GetTy() == rightTy.GetTy()); if (leftTy.IsTupleTy()) { new_elems.push_back(create_merged_tuple(leftTy, rightTy)); } else { new_elems.push_back(leftTy.GetSize() > rightTy.GetSize() ? leftTy : rightTy); } } return CreateTuple(bpftrace_.structs.AddTuple(new_elems)); } void SemanticAnalyser::resolve_struct_type(SizedType &type, const location &loc) { const SizedType *inner_type = &type; int pointer_level = 0; while (inner_type->IsPtrTy()) { inner_type = inner_type->GetPointeeTy(); pointer_level++; } if (inner_type->IsRecordTy() && inner_type->GetStruct().expired()) { auto struct_type = bpftrace_.structs.Lookup(inner_type->GetName()); if (struct_type.expired()) LOG(ERROR, loc, err_) << "Cannot resolve unknown type \"" << inner_type->GetName() << "\"\n"; type = CreateRecord(inner_type->GetName(), struct_type); while (pointer_level > 0) { type = CreatePointer(type); pointer_level--; } } } bool SemanticAnalyser::has_error() const { const auto &errors = err_.str(); return !errors.empty(); } Pass CreateSemanticPass() { auto fn = [](PassContext &ctx) { auto semantics = SemanticAnalyser(ctx.ast_ctx, ctx.b, !ctx.b.cmd_.empty()); int err = semantics.analyse(); if (err) return PassResult::Error("Semantic", err); return PassResult::Success(); }; return Pass("Semantic", fn); }; Expression *SemanticAnalyser::dereference_if_needed(Expression *expr) { visit(expr); if (expr->type.IsRefTy()) { expr->type.IntoPointer(); const SizedType &ptr_type = expr->type; Expression *ptr_expr = expr; Unop *deref_expr = ctx_.make_node( Operator::MUL, ptr_expr, false, ptr_expr->loc); deref_expr->type = *ptr_type.GetPointeeTy(); deref_expr->type.is_internal = ptr_type.is_internal; deref_expr->type.SetAS(ptr_type.GetAS()); if (ptr_type.IsCtxAccess()) deref_expr->type.MarkCtxAccess(); return deref_expr; } return expr; } variable *SemanticAnalyser::find_variable(const std::string &var_ident) { if (auto *scope = find_variable_scope(var_ident)) { return &variables_[scope][var_ident]; } return nullptr; } Node *SemanticAnalyser::find_variable_scope(const std::string &var_ident) { for (auto scope : scope_stack_) { if (auto search_val = variables_[scope].find(var_ident); search_val != variables_[scope].end()) { return scope; } } return nullptr; } } // namespace bpftrace::ast bpftrace-0.23.2/src/ast/passes/semantic_analyser.h000066400000000000000000000143511477746507000221370ustar00rootroot00000000000000#pragma once #include #include #include #include "ast/pass_manager.h" #include "ast/visitor.h" #include "bpffeature.h" #include "bpftrace.h" #include "collect_nodes.h" #include "config.h" #include "types.h" namespace bpftrace { namespace ast { struct variable { SizedType type; bool can_resize; bool was_assigned; }; class PassTracker { public: void mark_final_pass() { is_final_pass_ = true; } bool is_final_pass() const { return is_final_pass_; } void inc_num_unresolved() { num_unresolved_++; } void reset_num_unresolved() { num_unresolved_ = 0; } int get_num_unresolved() const { return num_unresolved_; } int get_num_passes() const { return num_passes_; } void inc_num_passes() { num_passes_++; } private: bool is_final_pass_ = false; int num_unresolved_ = 0; int num_passes_ = 1; }; class SemanticAnalyser : public Visitor { public: explicit SemanticAnalyser(ASTContext &ctx, BPFtrace &bpftrace, std::ostream &out = std::cerr, bool has_child = true, bool listing = false) : Visitor(ctx), bpftrace_(bpftrace), out_(out), listing_(listing), has_child_(has_child) { } explicit SemanticAnalyser(ASTContext &ctx, BPFtrace &bpftrace, bool has_child) : SemanticAnalyser(ctx, bpftrace, std::cerr, has_child) { } explicit SemanticAnalyser(ASTContext &ctx, BPFtrace &bpftrace, bool has_child, bool listing) : SemanticAnalyser(ctx, bpftrace, std::cerr, has_child, listing) { } using Visitor::visit; void visit(Integer &integer); void visit(PositionalParameter ¶m); void visit(String &string); void visit(StackMode &mode); void visit(Identifier &identifier); void visit(Builtin &builtin); void visit(Call &call); void visit(Sizeof &szof); void visit(Offsetof &offof); void visit(Map &map); void visit(Variable &var); void visit(Binop &binop); void visit(Unop &unop); void visit(While &while_block); void visit(For &f); void visit(Jump &jump); void visit(Ternary &ternary); void visit(FieldAccess &acc); void visit(ArrayAccess &arr); void visit(Cast &cast); void visit(Tuple &tuple); void visit(ExprStatement &expr); void visit(AssignMapStatement &assignment); void visit(AssignVarStatement &assignment); void visit(AssignConfigVarStatement &assignment); void visit(VarDeclStatement &decl); void visit(If &if_node); void visit(Unroll &unroll); void visit(Predicate &pred); void visit(AttachPoint &ap); void visit(Probe &probe); void visit(Config &config); void visit(Block &block); void visit(Subprog &subprog); int analyse(); private: PassTracker pass_tracker_; BPFtrace &bpftrace_; std::ostream &out_; std::ostringstream err_; bool listing_; bool is_final_pass() const; bool is_first_pass() const; bool check_assignment(const Call &call, bool want_map, bool want_var, bool want_map_key); [[nodiscard]] bool check_nargs(const Call &call, size_t expected_nargs); [[nodiscard]] bool check_varargs(const Call &call, size_t min_nargs, size_t max_nargs); bool check_arg(const Call &call, Type type, int arg_num, bool want_literal = false, bool fail = true); bool check_symbol(const Call &call, int arg_num); bool check_available(const Call &call, const AttachPoint &ap); void check_stack_call(Call &call, bool kernel); Probe *get_probe(const location &loc, std::string name = ""); bool is_valid_assignment(const Expression *target, const Expression *expr); SizedType *get_map_type(const Map &map); SizedType *get_map_key_type(const Map &map); void assign_map_type(const Map &map, const SizedType &type); SizedType create_key_type(const SizedType &expr_type, const location &loc); void update_current_key(SizedType ¤t_key_type, const SizedType &new_key_type); void validate_new_key(const SizedType ¤t_key_type, const SizedType &new_key_type, const std::string &map_ident, const location &loc); bool update_string_size(SizedType &type, const SizedType &new_type); SizedType create_merged_tuple(const SizedType &blah_type, const SizedType &prev_type); void validate_map_key(const SizedType &key, const location &loc); void resolve_struct_type(SizedType &type, const location &loc); void builtin_args_tracepoint(AttachPoint *attach_point, Builtin &builtin); ProbeType single_provider_type(Probe *probe); AddrSpace find_addrspace(ProbeType pt); void binop_ptr(Binop &op); void binop_int(Binop &op); void binop_array(Binop &op); Expression *dereference_if_needed(Expression *expr); bool has_error() const; bool in_loop(void) { return loop_depth_ > 0; }; void accept_statements(StatementList &stmts); // At the moment we iterate over the stack from top to bottom as variable // shadowing is not supported. std::vector scope_stack_; Node *top_level_node_ = nullptr; // Holds the function currently being visited by this SemanticAnalyser. std::string func_; // Holds the function argument index currently being visited by this // SemanticAnalyser. int func_arg_idx_ = -1; variable *find_variable(const std::string &var_ident); Node *find_variable_scope(const std::string &var_ident); std::map> variables_; std::map> variable_decls_; std::map> for_vars_referenced_; std::map map_val_; std::map map_key_; uint32_t loop_depth_ = 0; bool has_begin_probe_ = false; bool has_end_probe_ = false; bool has_child_ = false; bool has_pos_param_ = false; }; Pass CreateSemanticPass(); } // namespace ast } // namespace bpftrace bpftrace-0.23.2/src/ast/signal.cpp000066400000000000000000000023421477746507000167450ustar00rootroot00000000000000#include "ast/signal_bt.h" #include #include namespace bpftrace { static std::map signals = { { "SIGABRT", SIGABRT }, { "SIGALRM", SIGALRM }, { "SIGBUS", SIGBUS }, { "SIGCHLD", SIGCHLD }, { "SIGCONT", SIGCONT }, { "SIGFPE", SIGFPE }, { "SIGHUP", SIGHUP }, { "SIGILL", SIGILL }, { "SIGINT", SIGINT }, { "SIGKILL", SIGKILL }, { "SIGPIPE", SIGPIPE }, { "SIGPOLL", SIGPOLL }, { "SIGQUIT", SIGQUIT }, { "SIGSEGV", SIGSEGV }, { "SIGSTOP", SIGSTOP }, { "SIGSYS", SIGSYS }, { "SIGTERM", SIGTERM }, { "SIGTRAP", SIGTRAP }, { "SIGTSTP", SIGTSTP }, { "SIGTTIN", SIGTTIN }, { "SIGTTOU", SIGTTOU }, { "SIGURG", SIGURG }, { "SIGUSR1", SIGUSR1 }, { "SIGUSR2", SIGUSR2 }, { "SIGVTALRM", SIGVTALRM }, { "SIGWINCH", SIGWINCH }, { "SIGXCPU", SIGXCPU }, { "SIGXFSZ", SIGXFSZ }, }; int signal_name_to_num(const std::string &signal) { if (signal.empty()) { return -1; } std::string sig(signal); std::for_each(sig.begin(), sig.end(), [](char &c) { c = ::toupper(c); }); if (sig[0] != 'S') { sig.insert(0, "SIG"); } auto s = signals.find(sig); if (s != signals.end()) return s->second; return -1; } } // namespace bpftrace bpftrace-0.23.2/src/ast/signal_bt.h000066400000000000000000000003341477746507000170760ustar00rootroot00000000000000// This file is name "signal_bt.h" so it doesn't shadow "" #pragma once #include #include namespace bpftrace { int signal_name_to_num(const std::string &signal); } // namespace bpftrace bpftrace-0.23.2/src/ast/visitor.h000066400000000000000000000234701477746507000166410ustar00rootroot00000000000000#pragma once #include #include "ast/ast.h" namespace bpftrace::ast { // Visitor for fully-static visitation. // // This uses CRTP to make all calls static, while still allowing the entrypoint // for a single visitor to be dispatched dynamically. The implementation may // optionally provide individual `visit` methods (matching either pointers or // references, the latter preferred), or `replace` methods (matching just the // relevant pointer types and returning the same) which can return new nodes // when replacement is required. This makes it simple to write self-contained // passes that rewrite part of the AST. // // Note that replacement is not currently possible for aggregate types (e.g. // std::vector), and these will still be visited (and possible replaced on an // item-side basis). If modification of these is needed, then the visitor // should do replacement inline within the owner of the list, i.e. replace the // full Block node, rather than attempting to intersect the list. template class Visitor { public: Visitor(ASTContext &ctx) : ctx_(ctx) { } // See above; specific replace methods may be defined. template T *replace(T *node, [[maybe_unused]] R *result) { return node; } // visit methods are used to traverse the graph, and are provided a reference // to the underlying node. The visit is invoked *before* the replace call, // and can directly consume and modify the results of the visit. R visit(Integer &integer __attribute__((__unused__))) { return default_value(); } R visit(PositionalParameter &integer __attribute__((__unused__))) { return default_value(); } R visit(String &string __attribute__((__unused__))) { return default_value(); } R visit(Builtin &builtin __attribute__((__unused__))) { return default_value(); } R visit(Identifier &identifier __attribute__((__unused__))) { return default_value(); } R visit(StackMode &mode __attribute__((__unused__))) { return default_value(); } R visit(Variable &var __attribute__((__unused__))) { return default_value(); } R visit(SubprogArg &subprog_arg __attribute__((__unused__))) { return default_value(); } R visit(AttachPoint &ap __attribute__((__unused__))) { return default_value(); } R visit(Call &call) { return visitImpl(call.vargs); } R visit(Sizeof &szof) { return visitAndReplace(&szof.expr); } R visit(Offsetof &ofof) { return visitAndReplace(&ofof.expr); } R visit(Map &map) { return visitAndReplace(&map.key_expr); } R visit(Binop &binop) { visitAndReplace(&binop.left); visitAndReplace(&binop.right); return default_value(); } R visit(Unop &unop) { return visitAndReplace(&unop.expr); } R visit(Ternary &ternary) { visitAndReplace(&ternary.cond); visitAndReplace(&ternary.left); visitAndReplace(&ternary.right); return default_value(); } R visit(FieldAccess &acc) { return visitAndReplace(&acc.expr); } R visit(ArrayAccess &arr) { visitAndReplace(&arr.expr); visitAndReplace(&arr.indexpr); return default_value(); } R visit(Cast &cast) { return visitAndReplace(&cast.expr); } R visit(Tuple &tuple) { return visitImpl(tuple.elems); } R visit(ExprStatement &expr) { return visitAndReplace(&expr.expr); } R visit(AssignMapStatement &assignment) { visitAndReplace(&assignment.map); visitAndReplace(&assignment.expr); return default_value(); } R visit(AssignVarStatement &assignment) { visitAndReplace(&assignment.var); visitAndReplace(&assignment.expr); return default_value(); } R visit(AssignConfigVarStatement &assignment) { return visitAndReplace(&assignment.expr); } R visit(VarDeclStatement &decl) { return visitAndReplace(&decl.var); } R visit(If &if_node) { visitAndReplace(&if_node.cond); visitAndReplace(&if_node.if_block); visitAndReplace(&if_node.else_block); return default_value(); } R visit(Jump &jump) { return visitAndReplace(&jump.return_value); } R visit(Unroll &unroll) { visitAndReplace(&unroll.expr); visitAndReplace(&unroll.block); return default_value(); } R visit(While &while_block) { visitAndReplace(&while_block.cond); visitAndReplace(&while_block.block); return default_value(); } R visit(For &for_loop) { visitAndReplace(&for_loop.decl); visitAndReplace(&for_loop.expr); visitImpl(for_loop.stmts); return default_value(); } R visit(Predicate &pred) { return visitAndReplace(&pred.expr); } R visit(Probe &probe) { visitImpl(probe.attach_points); visitAndReplace(&probe.pred); visitAndReplace(&probe.block); return default_value(); } R visit(Config &config) { visitImpl(config.stmts); return default_value(); } R visit(Block &block) { visitImpl(block.stmts); return default_value(); } R visit(Subprog &subprog) { visitImpl(subprog.args); visitImpl(subprog.stmts); return default_value(); } R visit(Program &program) { visitImpl(program.functions); visitImpl(program.probes); visitAndReplace(&program.config); return default_value(); } // Temporarily allow visits to expression and statement references. This // does not permit the modification of the underlying value, but does allow // the existing passes to continue to work (which do not modify anything, so // this is not a problem for the time being). template R visit(T &t) { T *ptr = &t; if constexpr (!std::is_void_v) { auto rval = visitAndReplace(&ptr); assert(ptr == &t); // Should not be modified. return rval; } else { visitAndReplace(&ptr); assert(ptr == &t); // See above. } } // Automatically unpack and dispatch all variant and vector types into the // suitable visitor method. // // In order to automatically replace a variant, e.g. change from type A to // type B, it is necessary to provide a replace method that accepts that // variant type directly. This could still dispatch via the standard visit // function, which could e.g. return the replacement pointer, but this would // be a single specialized pass for this case. template R visit(std::variant var) { return std::visit( [this](auto &value) -> R { return visitAndReplace(&value); }, var); } template R visit(std::vector &var) { for (auto &value : var) { visitAndReplace(&value); } return default_value(); } template R visit(std::optional &var) { if (var) { return visitAndReplace(&(*var)); } return default_value(); } // This is a convenience for dispatching directly from a pointer type, it // does not allow for replacement of this specific instance. template R visit(T *ptr) { if (ptr) return visitImpl(*ptr); return default_value(); } private: template R visitImpl(T &t) { Impl *impl = static_cast(this); return impl->visit(t); } template R visitAndReplace(T **t) { auto orig = *t; // Prior to replacement. Impl *impl = static_cast(this); if constexpr (!std::is_void_v) { auto rval = impl->visit(orig); *t = impl->replace(orig, &rval); return rval; } else { impl->visit(orig); *t = impl->replace(orig, nullptr); return default_value(); } } R default_value() { if constexpr (!std::is_void_v) { return R(); } } // These are the runtime-type adaptors that are currently required for // Expression and Statement, but can be removed by encoding this type // information into the AST directly. template R tryVisitAndReplace(Orig **node) { if (auto *t = dynamic_cast(*node)) { if constexpr (!std::is_void_v) { auto rval = visitAndReplace(&t); *node = static_cast(t); // Copy the modification. return rval; } else { visitAndReplace(&t); *node = static_cast(t); // See above. return; } } else if constexpr (sizeof...(Ts) != 0) { return tryVisitAndReplace(node); } return default_value(); } R visitAndReplace(Expression **expr) { return tryVisitAndReplace(expr); } R visitAndReplace(Statement **stmt) { return tryVisitAndReplace(stmt); } protected: ASTContext &ctx_; }; } // namespace bpftrace::ast bpftrace-0.23.2/src/attached_probe.cpp000066400000000000000000001126561477746507000176570ustar00rootroot00000000000000#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "attached_probe.h" #include "bpftrace.h" #include "disasm.h" #include "log.h" #include "probe_matcher.h" #include "usdt.h" #include "utils.h" namespace bpftrace { bpf_probe_attach_type attachtype(ProbeType t) { // clang-format off switch (t) { case ProbeType::kprobe: return BPF_PROBE_ENTRY; break; case ProbeType::kretprobe: return BPF_PROBE_RETURN; break; case ProbeType::special: return BPF_PROBE_ENTRY; break; case ProbeType::uprobe: return BPF_PROBE_ENTRY; break; case ProbeType::uretprobe: return BPF_PROBE_RETURN; break; case ProbeType::usdt: return BPF_PROBE_ENTRY; break; default: LOG(BUG) << "invalid probe attachtype \"" << t << "\""; } // clang-format on } libbpf::bpf_prog_type progtype(ProbeType t) { switch (t) { // clang-format off case ProbeType::special: return libbpf::BPF_PROG_TYPE_RAW_TRACEPOINT; break; case ProbeType::kprobe: return libbpf::BPF_PROG_TYPE_KPROBE; break; case ProbeType::kretprobe: return libbpf::BPF_PROG_TYPE_KPROBE; break; case ProbeType::uprobe: return libbpf::BPF_PROG_TYPE_KPROBE; break; case ProbeType::uretprobe: return libbpf::BPF_PROG_TYPE_KPROBE; break; case ProbeType::usdt: return libbpf::BPF_PROG_TYPE_KPROBE; break; case ProbeType::tracepoint: return libbpf::BPF_PROG_TYPE_TRACEPOINT; break; case ProbeType::profile: return libbpf::BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::interval: return libbpf::BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::software: return libbpf::BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::watchpoint: return libbpf::BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::asyncwatchpoint: return libbpf::BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::hardware: return libbpf::BPF_PROG_TYPE_PERF_EVENT; break; case ProbeType::fentry: return libbpf::BPF_PROG_TYPE_TRACING; break; case ProbeType::fexit: return libbpf::BPF_PROG_TYPE_TRACING; break; case ProbeType::iter: return libbpf::BPF_PROG_TYPE_TRACING; break; case ProbeType::rawtracepoint: return libbpf::BPF_PROG_TYPE_RAW_TRACEPOINT; break; // clang-format on case ProbeType::invalid: LOG(BUG) << "program type invalid"; } return {}; // unreached } std::string progtypeName(libbpf::bpf_prog_type t) { switch (t) { // clang-format off case libbpf::BPF_PROG_TYPE_KPROBE: return "BPF_PROG_TYPE_KPROBE"; break; case libbpf::BPF_PROG_TYPE_TRACEPOINT: return "BPF_PROG_TYPE_TRACEPOINT"; break; case libbpf::BPF_PROG_TYPE_PERF_EVENT: return "BPF_PROG_TYPE_PERF_EVENT"; break; case libbpf::BPF_PROG_TYPE_TRACING: return "BPF_PROG_TYPE_TRACING"; break; // clang-format on default: LOG(BUG) << "invalid program type: " << t; } } void AttachedProbe::attach_fentry() { if (progfd_ < 0) return; tracing_fd_ = bpf_raw_tracepoint_open(nullptr, progfd_); if (tracing_fd_ < 0) { throw FatalUserException("Error attaching probe: " + probe_.name); } } int AttachedProbe::detach_fentry() { close(tracing_fd_); return 0; } void AttachedProbe::attach_iter() { linkfd_ = bpf_link_create(progfd_, 0, static_cast( libbpf::BPF_TRACE_ITER), nullptr); if (linkfd_ < 0) { throw FatalUserException("Error attaching probe: " + probe_.name); } } int AttachedProbe::detach_iter() { close(linkfd_); return 0; } void AttachedProbe::attach_raw_tracepoint() { tracing_fd_ = bpf_raw_tracepoint_open(probe_.attach_point.c_str(), progfd_); if (tracing_fd_ < 0) { if (tracing_fd_ == -ENOENT) throw FatalUserException("Probe does not exist: " + probe_.name); else if (tracing_fd_ == -EINVAL) throw FatalUserException("Error attaching probe: " + probe_.name + ", maybe trying to access arguments beyond " "what's available in this tracepoint"); else throw FatalUserException("Error attaching probe: " + probe_.name); } } int AttachedProbe::detach_raw_tracepoint() { close(tracing_fd_); return 0; } AttachedProbe::AttachedProbe(Probe &probe, const BpfProgram &prog, BPFtrace &bpftrace) : probe_(probe), progfd_(prog.fd()), bpftrace_(bpftrace) { LOG(V1) << "Attaching " << probe_.orig_name; switch (probe_.type) { case ProbeType::kprobe: attach_kprobe(); break; case ProbeType::kretprobe: attach_kprobe(); break; case ProbeType::tracepoint: attach_tracepoint(); break; case ProbeType::profile: attach_profile(); break; case ProbeType::interval: attach_interval(); break; case ProbeType::software: attach_software(); break; case ProbeType::hardware: attach_hardware(); break; case ProbeType::fentry: case ProbeType::fexit: attach_fentry(); break; case ProbeType::iter: attach_iter(); break; case ProbeType::rawtracepoint: attach_raw_tracepoint(); break; default: LOG(BUG) << "invalid attached probe type \"" << probe_.type << "\""; } } AttachedProbe::AttachedProbe(Probe &probe, const BpfProgram &prog, int pid, BPFtrace &bpftrace, bool safe_mode) : probe_(probe), progfd_(prog.fd()), bpftrace_(bpftrace) { switch (probe_.type) { case ProbeType::usdt: attach_usdt(pid, *bpftrace_.feature_); break; case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: attach_watchpoint(pid, probe.mode); break; case ProbeType::uprobe: case ProbeType::uretprobe: attach_uprobe(pid, safe_mode); break; default: LOG(BUG) << "invalid attached probe type \"" << probe_.type << "\""; } } AttachedProbe::~AttachedProbe() { int err = 0; for (int perf_event_fd : perf_event_fds_) { err = bpf_close_perf_event_fd(perf_event_fd); if (err) LOG(WARNING) << "failed to close perf event FDs for probe: " << probe_.name; } err = 0; switch (probe_.type) { case ProbeType::kprobe: case ProbeType::kretprobe: if (probe_.funcs.empty()) err = bpf_detach_kprobe(eventname().c_str()); else close(linkfd_); break; case ProbeType::fentry: case ProbeType::fexit: err = detach_fentry(); break; case ProbeType::iter: err = detach_iter(); break; case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: if (usdt_destructor_) usdt_destructor_(); err = bpf_detach_uprobe(eventname().c_str()); break; case ProbeType::tracepoint: err = bpf_detach_tracepoint(probe_.path.c_str(), eventname().c_str()); break; case ProbeType::special: case ProbeType::profile: case ProbeType::interval: case ProbeType::software: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::hardware: break; case ProbeType::rawtracepoint: err = detach_raw_tracepoint(); break; case ProbeType::invalid: LOG(BUG) << "invalid attached probe type \"" << probe_.type << "\" at destructor"; } if (err) LOG(WARNING) << "failed to detach probe: " << probe_.name; if (close_progfd_ && progfd_ >= 0) close(progfd_); } const Probe &AttachedProbe::probe() const { return probe_; } int AttachedProbe::progfd() const { return progfd_; } std::string AttachedProbe::eventprefix() const { switch (attachtype(probe_.type)) { case BPF_PROBE_ENTRY: return "p_"; case BPF_PROBE_RETURN: return "r_"; } return {}; // unreached } std::string AttachedProbe::eventname() const { std::ostringstream offset_str; std::string index_str = "_" + std::to_string(probe_.index); switch (probe_.type) { case ProbeType::kprobe: case ProbeType::kretprobe: case ProbeType::rawtracepoint: offset_str << std::hex << offset_; return eventprefix() + sanitise_bpf_program_name(probe_.attach_point) + "_" + offset_str.str() + index_str; case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: offset_str << std::hex << offset_; return eventprefix() + sanitise_bpf_program_name(probe_.path) + "_" + offset_str.str() + index_str; case ProbeType::tracepoint: return probe_.attach_point; default: LOG(BUG) << "invalid eventname probe \"" << probe_.type << "\""; } } static uint64_t resolve_offset(const std::string &path, const std::string &symbol, uint64_t loc) { bcc_symbol bcc_sym; if (bcc_resolve_symname( path.c_str(), symbol.c_str(), loc, 0, nullptr, &bcc_sym)) throw FatalUserException("Could not resolve symbol: " + path + ":" + symbol); // Have to free sym.module, see: // https://github.com/iovisor/bcc/blob/ba73657cb8c4dab83dfb89eed4a8b3866255569a/src/cc/bcc_syms.h#L98-L99 if (bcc_sym.module) ::free(const_cast(bcc_sym.module)); return bcc_sym.offset; } static constexpr std::string_view hint_unsafe = "\nUse --unsafe to force attachment. WARNING: This option could lead to " "data corruption in the target process."; static constexpr std::string_view hint_symbol_source = "\nUse config 'symbol_source = \"symbol_table\"' in case of bad DebugInfo."; static void check_alignment(std::string &orig_name, std::string &path, std::string &symbol, uint64_t sym_offset, uint64_t func_offset, bool safe_mode, ProbeType type) { Disasm dasm(path); AlignState aligned = dasm.is_aligned(sym_offset, func_offset); std::string tmp = path + ":" + symbol + "+" + std::to_string(func_offset); switch (aligned) { case AlignState::Ok: return; case AlignState::NotAlign: if (safe_mode) { auto msg = "Could not add " + probetypeName(type) + " into middle of instruction: " + tmp + std::string{ hint_unsafe }; if (orig_name.find('*') != std::string::npos) msg += hint_symbol_source; throw FatalUserException(std::move(msg)); } else { std::string_view hint; if (orig_name.find('*') != std::string::npos) hint = hint_symbol_source; LOG(WARNING) << "Unsafe " << type << " in the middle of the instruction: " << tmp << hint; } break; case AlignState::Fail: if (safe_mode) throw FatalUserException("Failed to check if " + probetypeName(type) + " is in proper place: " + tmp + std::string{ hint_unsafe }); else LOG(WARNING) << "Unchecked " << type << ": " << tmp; break; case AlignState::NotSupp: if (safe_mode) throw FatalUserException("Can't check if " + probetypeName(type) + " is in proper place (compiled without " "(k|u)probe offset support): " + tmp + std::string{ hint_unsafe }); else LOG(WARNING) << "Unchecked " << type << " : " << tmp; break; } } bool AttachedProbe::resolve_offset_uprobe(bool safe_mode, bool has_multiple_aps) { struct bcc_symbol_option option = {}; struct symbol sym = {}; std::string &symbol = probe_.attach_point; uint64_t func_offset = probe_.func_offset; sym.name = ""; option.use_debug_file = 1; option.use_symbol_type = BCC_SYM_ALL_TYPES ^ (1 << STT_NOTYPE); if (symbol.empty()) { sym.address = probe_.address; bcc_elf_foreach_sym(probe_.path.c_str(), sym_address_cb, &option, &sym); if (!sym.start) { if (safe_mode) { std::stringstream ss; ss << "0x" << std::hex << probe_.address; throw FatalUserException("Could not resolve address: " + probe_.path + ":" + ss.str()); } else { LOG(WARNING) << "Could not determine instruction boundary for " << probe_.name << " (binary appears stripped). Misaligned probes " "can lead to tracee crashes!"; offset_ = probe_.address; return true; } } symbol = sym.name; func_offset = probe_.address - sym.start; } else { sym.name = symbol; bcc_elf_foreach_sym(probe_.path.c_str(), sym_name_cb, &option, &sym); if (!sym.start) { const std::string msg = "Could not resolve symbol: " + probe_.path + ":" + symbol; auto missing_probes = bpftrace_.config_.get( ConfigKeyMissingProbes::default_); if (!has_multiple_aps || missing_probes == ConfigMissingProbes::error) { throw FatalUserException(msg + ", cannot attach probe."); } else { if (missing_probes == ConfigMissingProbes::warn) LOG(WARNING) << msg << ", skipping probe."; return 0; } } } if (probe_.type == ProbeType::uretprobe && func_offset != 0) { throw FatalUserException("uretprobes cannot be attached at function " "offset. (address resolved to: " + symbol + "+" + std::to_string(func_offset) + ")"); } if (sym.size == 0 && func_offset == 0) { if (safe_mode) { std::stringstream msg; msg << "Could not determine boundary for " << sym.name << " (symbol has size 0)."; if (probe_.orig_name == probe_.name) { msg << hint_unsafe; throw FatalUserException(msg.str()); } else { LOG(WARNING) << msg.str() << " Skipping attachment." << hint_unsafe; } return false; } } else if (func_offset >= sym.size) { throw FatalUserException("Offset outside the function bounds ('" + symbol + "' size is " + std::to_string(sym.size) + ")"); } uint64_t sym_offset = resolve_offset(probe_.path, probe_.attach_point, probe_.loc); offset_ = sym_offset + func_offset; // If we are not aligned to the start of the symbol, // check if we are on the instruction boundary. if (func_offset == 0) return true; check_alignment(probe_.orig_name, probe_.path, symbol, sym_offset, func_offset, safe_mode, probe_.type); return true; } void AttachedProbe::resolve_offset_kprobe() { offset_ = probe_.func_offset; // If we are using only the symbol, we don't need to check the offset. bool is_symbol_kprobe = !probe_.attach_point.empty(); if (is_symbol_kprobe && probe_.func_offset == 0) return; // Setup the symbol to resolve, either using the address or the name. struct symbol sym = {}; if (is_symbol_kprobe) sym.name = probe_.attach_point; else sym.address = probe_.address; auto path = find_vmlinux(&sym); if (!path.has_value()) { if (!is_symbol_kprobe) throw FatalUserException("Could not resolve address: " + std::to_string(probe_.address)); LOG(V1) << "Could not resolve symbol " << probe_.attach_point << ". Skipping usermode offset checking."; LOG(V1) << "The kernel will verify the safety of the location but " "will also allow the offset to be in a different symbol."; return; } // Populate probe_ fields according to the resolved symbol. if (is_symbol_kprobe) { probe_.address = sym.start + probe_.func_offset; } else { probe_.attach_point = std::move(sym.name); if (__builtin_sub_overflow(probe_.address, sym.start, &probe_.func_offset)) LOG(BUG) << "Offset before the function bounds ('" << probe_.attach_point << "' address is " << std::to_string(sym.start) << ")"; offset_ = probe_.func_offset; // Set the name of the probe to the resolved symbol+offset, so that failure // to attach can be ignored if the user set ConfigMissingProbes::warn. probe_.name = "kprobe:" + probe_.attach_point + "+" + std::to_string(probe_.func_offset); } if (probe_.func_offset >= sym.size) throw FatalUserException("Offset outside the function bounds ('" + probe_.attach_point + "' size is " + std::to_string(sym.size) + ")"); } void AttachedProbe::attach_multi_kprobe() { BPFTRACE_LIBBPF_OPTS(bpf_link_create_opts, opts); std::vector syms; unsigned int i = 0; for (i = 0; i < probe_.funcs.size(); i++) { syms.push_back(probe_.funcs[i].c_str()); } opts.kprobe_multi.syms = syms.data(); opts.kprobe_multi.cnt = syms.size(); opts.kprobe_multi.flags = probe_.type == ProbeType::kretprobe ? BPF_F_KPROBE_MULTI_RETURN : 0; if (bt_verbose) { LOG(V1) << "Attaching to " << probe_.funcs.size() << " functions"; for (i = 0; i < opts.kprobe_multi.cnt; i++) { LOG(V1) << " " << syms[i]; } } linkfd_ = bpf_link_create(progfd_, 0, static_cast( libbpf::BPF_TRACE_KPROBE_MULTI), &opts); if (linkfd_ < 0) { throw FatalUserException("Error attaching probe: " + probe_.name); } } void AttachedProbe::attach_kprobe() { if (!probe_.funcs.empty()) { attach_multi_kprobe(); return; } // Construct a string containing "module:function." // Also log a warning or throw an error if the module doesn't exist, // before attempting to attach. // Note that we do not pass vmlinux, if it is specified. std::string funcname = probe_.attach_point; const std::string &modname = probe_.path; if ((modname.length() > 0) && modname != "vmlinux") { if (!is_module_loaded(modname)) { std::string message = "specified module " + modname + " in probe " + probe_.name + " is not loaded."; if (probe_.orig_name != probe_.name) { // Wildcard usage just gets a warning LOG(WARNING) << message; } else { // Explicitly specified modules should fail throw FatalUserException("Error attaching probe: " + probe_.name + ": " + message); } } funcname = modname + ":" + funcname; } // If the user requested to ignore warnings on non-existing probes and the // function is not traceable, do not even try to attach as that would yield // warnings from BCC which we don't want to see. if (bpftrace_.config_.get(ConfigKeyMissingProbes::default_) == ConfigMissingProbes::ignore && probe_.name != probe_.orig_name && (funcname != "" || probe_.address != 0) && !bpftrace_.is_traceable_func(funcname)) return; // The kprobe can either be defined by a symbol+offset or an address: // For symbol+offset kprobe, we need to check the validity of the offset. // For address kprobe, we need to resolve into the symbol+offset and // populate `funcname` with the results stored back in the probe_. bool is_symbol_kprobe = !probe_.attach_point.empty(); resolve_offset_kprobe(); if (!is_symbol_kprobe) funcname += probe_.attach_point; LOG(V1) << "bpf_attach_kprobe(" << progfd_ << ", " << probe_.type << ", " << eventname() << ", " << funcname << ", " << offset_ << ", 0)"; int perf_event_fd = bpf_attach_kprobe(progfd_, attachtype(probe_.type), eventname().c_str(), funcname.c_str(), offset_, 0); if (perf_event_fd < 0) { if (probe_.orig_name != probe_.name && bpftrace_.config_.get(ConfigKeyMissingProbes::default_) == ConfigMissingProbes::warn) { // a wildcard expansion couldn't probe something, just print a warning // as this is normal for some kernel functions (eg, do_debug()) LOG(WARNING) << "could not attach probe " << probe_.name << ", skipping."; } else { if (errno == EILSEQ) LOG(ERROR) << "Possible attachment attempt in the middle of an " "instruction, try a different offset."; // an explicit match failed, so fail as the user must have wanted it throw FatalUserException("Error attaching probe: " + probe_.name); } } perf_event_fds_.push_back(perf_event_fd); } #ifdef HAVE_LIBBPF_UPROBE_MULTI struct bcc_sym_cb_data { std::vector &syms; std::set &offsets; }; static int bcc_sym_cb(const char *symname, uint64_t start, uint64_t, void *p) { struct bcc_sym_cb_data *data = static_cast(p); std::vector &syms = data->syms; if (std::binary_search(syms.begin(), syms.end(), symname)) { data->offsets.insert(start); } return 0; } struct addr_offset { uint64_t addr; uint64_t offset; }; static int bcc_load_cb(uint64_t v_addr, uint64_t mem_sz, uint64_t file_offset, void *p) { std::vector *addrs = static_cast *>(p); for (auto &a : *addrs) { if (a.addr >= v_addr && a.addr < (v_addr + mem_sz)) { a.offset = a.addr - v_addr + file_offset; } } return 0; } static void resolve_offset_uprobe_multi(const std::string &path, const std::string &probe_name, const std::vector &funcs, std::vector &syms, std::vector &offsets) { struct bcc_symbol_option option = {}; int err; // Parse symbols names into syms vector for (const std::string &func : funcs) { auto pos = func.find(':'); if (pos == std::string::npos) { throw FatalUserException("Error resolving probe: " + probe_name); } syms.push_back(func.substr(pos + 1)); } std::sort(std::begin(syms), std::end(syms)); option.use_debug_file = 1; option.use_symbol_type = BCC_SYM_ALL_TYPES ^ (1 << STT_NOTYPE); std::vector addrs; std::set set; struct bcc_sym_cb_data data = { .syms = syms, .offsets = set, }; // Resolve symbols into addresses err = bcc_elf_foreach_sym(path.c_str(), bcc_sym_cb, &option, &data); if (err) { throw FatalUserException("Failed to list symbols for probe: " + probe_name); } for (auto a : set) { struct addr_offset addr = { .addr = a, .offset = 0x0, }; addrs.push_back(addr); } // Translate addresses into offsets err = bcc_elf_foreach_load_section(path.c_str(), bcc_load_cb, &addrs); if (err) { throw FatalUserException("Failed to resolve symbols offsets for probe: " + probe_name); } for (auto a : addrs) { offsets.push_back(a.offset); } } void AttachedProbe::attach_multi_uprobe(int pid) { std::vector syms; std::vector offsets; unsigned int i; // Resolve probe_.funcs into offsets and syms vector resolve_offset_uprobe_multi( probe_.path, probe_.name, probe_.funcs, syms, offsets); // Attach uprobe through uprobe_multi link BPFTRACE_LIBBPF_OPTS(bpf_link_create_opts, opts); opts.uprobe_multi.path = probe_.path.c_str(); opts.uprobe_multi.offsets = offsets.data(); opts.uprobe_multi.cnt = offsets.size(); opts.uprobe_multi.flags = probe_.type == ProbeType::uretprobe ? BPF_F_UPROBE_MULTI_RETURN : 0; if (pid != 0) { opts.uprobe_multi.pid = pid; } if (bt_verbose) { LOG(V1) << "Attaching to " << probe_.funcs.size() << " functions"; for (i = 0; i < syms.size(); i++) { LOG(V1) << probe_.path << ":" << syms[i]; } } linkfd_ = bpf_link_create(progfd_, 0, static_cast( libbpf::BPF_TRACE_UPROBE_MULTI), &opts); if (linkfd_ < 0) { throw FatalUserException("Error attaching probe: " + probe_.name); } } #else void AttachedProbe::attach_multi_uprobe(int) { } #endif // HAVE_LIBBPF_UPROBE_MULTI void AttachedProbe::attach_uprobe(int pid, bool safe_mode) { if (!probe_.funcs.empty()) { attach_multi_uprobe(pid); return; } if (!resolve_offset_uprobe(safe_mode, probe_.orig_name != probe_.name)) return; int perf_event_fd = bpf_attach_uprobe(progfd_, attachtype(probe_.type), eventname().c_str(), probe_.path.c_str(), offset_, pid == 0 ? -1 : pid, 0); if (perf_event_fd < 0) { throw FatalUserException("Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } int AttachedProbe::usdt_sem_up_manual(const std::string &fn_name, void *ctx) { int err; #ifdef BCC_USDT_HAS_FULLY_SPECIFIED_PROBE if (probe_.ns == "") err = bcc_usdt_enable_probe(ctx, probe_.attach_point.c_str(), fn_name.c_str()); else err = bcc_usdt_enable_fully_specified_probe( ctx, probe_.ns.c_str(), probe_.attach_point.c_str(), fn_name.c_str()); #else err = bcc_usdt_enable_probe(ctx, probe_.attach_point.c_str(), fn_name.c_str()); #endif // BCC_USDT_HAS_FULLY_SPECIFIED_PROBE // Defer context destruction until probes are detached b/c context // destruction will decrement usdt semaphore count. usdt_destructor_ = [ctx]() { bcc_usdt_close(ctx); }; return err; } int AttachedProbe::usdt_sem_up_manual_addsem(int pid, const std::string &fn_name, void *ctx) { // NB: we are careful to capture by value here everything that will not // be available in AttachedProbe destructor. auto addsem = [this, fn_name](void *c, int16_t val) -> int { if (this->probe_.ns == "") return bcc_usdt_addsem_probe( c, this->probe_.attach_point.c_str(), fn_name.c_str(), val); else return bcc_usdt_addsem_fully_specified_probe( c, this->probe_.ns.c_str(), this->probe_.attach_point.c_str(), fn_name.c_str(), val); }; // Set destructor to decrement the semaphore count usdt_destructor_ = [pid, addsem]() { void *c = bcc_usdt_new_frompid(pid, nullptr); if (!c) return; addsem(c, -1); bcc_usdt_close(c); }; // Use semaphore increment API to avoid having to hold onto the usdt context // for the entire tracing session. Reason we do it this way instead of // holding onto usdt context is b/c each usdt context can take lots of memory // (~10MB). This, coupled with --usdt-file-activation and tracees that have a // forking model can cause bpftrace to use huge amounts of memory if we hold // onto the contexts. int err = addsem(ctx, +1); // Now close the context to save some memory bcc_usdt_close(ctx); return err; } int AttachedProbe::usdt_sem_up([[maybe_unused]] BPFfeature &feature, [[maybe_unused]] int pid, const std::string &fn_name, void *ctx) { // If we have BCC and kernel support for uprobe refcnt API, then we don't // need to do anything here. The kernel will increment the semaphore count // for us when we provide the semaphore offset. if (feature.has_uprobe_refcnt()) { bcc_usdt_close(ctx); return 0; } return usdt_sem_up_manual_addsem(pid, fn_name, ctx); } void AttachedProbe::attach_usdt(int pid, BPFfeature &feature) { struct bcc_usdt_location loc = {}; int err; void *ctx; // TODO: fn_name may need a unique suffix for each attachment on the same // probe: std::string fn_name = "probe_" + probe_.attach_point + "_1"; if (pid) { // FIXME when iovisor/bcc#2064 is merged, optionally pass probe_.path ctx = bcc_usdt_new_frompid(pid, nullptr); if (!ctx) throw FatalUserException( "Error initializing context for probe: " + probe_.name + ", for PID: " + std::to_string(pid)); } else { ctx = bcc_usdt_new_frompath(probe_.path.c_str()); if (!ctx) throw FatalUserException("Error initializing context for probe: " + probe_.name); } // Resolve location of usdt probe auto u = usdt_helper.find(pid, probe_.path, probe_.ns, probe_.attach_point); if (!u.has_value()) throw FatalUserException("Failed to find usdt probe: " + eventname()); probe_.path = u->path; err = bcc_usdt_get_location(ctx, probe_.ns.c_str(), probe_.attach_point.c_str(), probe_.usdt_location_idx, &loc); if (err) throw FatalUserException("Error finding location for probe: " + probe_.name); probe_.loc = loc.address; offset_ = resolve_offset(probe_.path, probe_.attach_point, probe_.loc); // Should be 0 if there's no semaphore // // Cast to 32 bits b/c kernel API only takes 32 bit offset [[maybe_unused]] auto semaphore_offset = static_cast( u->semaphore_offset); // Increment the semaphore count (will noop if no semaphore) // // NB: Do *not* use `ctx` after this call. It may either be open or closed, // depending on which path was taken. err = usdt_sem_up(feature, pid, fn_name, ctx); if (err) { throw FatalUserException( "Error finding or enabling probe: " + probe_.name + "\n Try using -p or --usdt-file-activation if there's USDT semaphores"); } int perf_event_fd = bpf_attach_uprobe(progfd_, attachtype(probe_.type), eventname().c_str(), probe_.path.c_str(), offset_, pid == 0 ? -1 : pid, semaphore_offset); if (perf_event_fd < 0) { if (pid) throw FatalUserException("Error attaching probe: " + probe_.name + ", to PID: " + std::to_string(pid)); else throw FatalUserException("Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } void AttachedProbe::attach_tracepoint() { int perf_event_fd = bpf_attach_tracepoint(progfd_, probe_.path.c_str(), eventname().c_str()); if (perf_event_fd < 0 && probe_.name == probe_.orig_name) { throw FatalUserException("Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } void AttachedProbe::attach_profile() { int pid = -1; int group_fd = -1; uint64_t period, freq; if (probe_.path == "hz") { period = 0; freq = probe_.freq; } else if (probe_.path == "s") { period = probe_.freq * 1e9; freq = 0; } else if (probe_.path == "ms") { period = probe_.freq * 1e6; freq = 0; } else if (probe_.path == "us") { period = probe_.freq * 1e3; freq = 0; } else { throw FatalUserException("invalid profile path \"" + probe_.path + "\""); } std::vector cpus = get_online_cpus(); for (int cpu : cpus) { int perf_event_fd = bpf_attach_perf_event(progfd_, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, period, freq, pid, cpu, group_fd); if (perf_event_fd < 0) { throw FatalUserException("Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } } void AttachedProbe::attach_interval() { int pid = -1; int group_fd = -1; int cpu = 0; uint64_t period = 0, freq = 0; if (probe_.path == "s") { period = probe_.freq * 1e9; } else if (probe_.path == "ms") { period = probe_.freq * 1e6; } else if (probe_.path == "us") { period = probe_.freq * 1e3; } else if (probe_.path == "hz") { freq = probe_.freq; } else { throw FatalUserException("invalid interval path \"" + probe_.path + "\""); } int perf_event_fd = bpf_attach_perf_event(progfd_, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, period, freq, pid, cpu, group_fd); if (perf_event_fd < 0) { throw FatalUserException("Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } void AttachedProbe::attach_software() { int pid = -1; int group_fd = -1; uint64_t period = probe_.freq; uint64_t defaultp = 1; uint32_t type = 0; // from linux/perf_event.h, with aliases from perf: for (auto &probeListItem : SW_PROBE_LIST) { if (probe_.path == probeListItem.path || probe_.path == probeListItem.alias) { type = probeListItem.type; defaultp = probeListItem.defaultp; } } if (period == 0) period = defaultp; std::vector cpus = get_online_cpus(); for (int cpu : cpus) { int perf_event_fd = bpf_attach_perf_event( progfd_, PERF_TYPE_SOFTWARE, type, period, 0, pid, cpu, group_fd); if (perf_event_fd < 0) { throw FatalUserException("Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } } void AttachedProbe::attach_hardware() { int pid = -1; int group_fd = -1; uint64_t period = probe_.freq; uint64_t defaultp = 1000000; uint32_t type = 0; // from linux/perf_event.h, with aliases from perf: for (auto &probeListItem : HW_PROBE_LIST) { if (probe_.path == probeListItem.path || probe_.path == probeListItem.alias) { type = probeListItem.type; defaultp = probeListItem.defaultp; } } if (period == 0) period = defaultp; std::vector cpus = get_online_cpus(); for (int cpu : cpus) { int perf_event_fd = bpf_attach_perf_event( progfd_, PERF_TYPE_HARDWARE, type, period, 0, pid, cpu, group_fd); if (perf_event_fd < 0) { throw FatalUserException("Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } } void AttachedProbe::attach_watchpoint(int pid, const std::string &mode) { struct perf_event_attr attr = {}; attr.type = PERF_TYPE_BREAKPOINT; attr.size = sizeof(struct perf_event_attr); attr.config = 0; attr.bp_type = HW_BREAKPOINT_EMPTY; for (const char c : mode) { if (c == 'r') attr.bp_type |= HW_BREAKPOINT_R; else if (c == 'w') attr.bp_type |= HW_BREAKPOINT_W; else if (c == 'x') attr.bp_type |= HW_BREAKPOINT_X; } attr.bp_addr = probe_.address; attr.bp_len = probe_.len; // Generate a notification every 1 event; we care about every event attr.sample_period = 1; std::vector cpus; if (pid >= 1) { cpus = { -1 }; } else { cpus = get_online_cpus(); pid = -1; } for (int cpu : cpus) { // We copy paste the code from bcc's bpf_attach_perf_event_raw here // because we need to know the exact error codes (and also we don't // want bcc's noisy error messages). int perf_event_fd = syscall( __NR_perf_event_open, &attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC); if (perf_event_fd < 0) { if (errno == ENOSPC) throw EnospcException("No more HW registers left"); else throw std::system_error(errno, std::generic_category(), "Error attaching probe: " + probe_.name); } if (ioctl(perf_event_fd, PERF_EVENT_IOC_SET_BPF, progfd_) != 0) { close(perf_event_fd); throw std::system_error(errno, std::generic_category(), "Error attaching probe: " + probe_.name); } if (ioctl(perf_event_fd, PERF_EVENT_IOC_ENABLE, 0) != 0) { close(perf_event_fd); throw std::system_error(errno, std::generic_category(), "Error attaching probe: " + probe_.name); } perf_event_fds_.push_back(perf_event_fd); } } } // namespace bpftrace bpftrace-0.23.2/src/attached_probe.h000066400000000000000000000053671477746507000173240ustar00rootroot00000000000000#pragma once #include #include #include #include #include "bpffeature.h" #include "bpfprogram.h" #include "btf.h" #include "config.h" #include "types.h" #include "usdt.h" #include namespace bpftrace { bpf_probe_attach_type attachtype(ProbeType t); libbpf::bpf_prog_type progtype(ProbeType t); std::string progtypeName(libbpf::bpf_prog_type t); class AttachedProbe { public: AttachedProbe(Probe &probe, const BpfProgram &prog, BPFtrace &bpftrace); AttachedProbe(Probe &probe, const BpfProgram &prog, int pid, BPFtrace &bpftrace, bool safe_mode = true); ~AttachedProbe(); AttachedProbe(const AttachedProbe &) = delete; AttachedProbe &operator=(const AttachedProbe &) = delete; const Probe &probe() const; int progfd() const; int linkfd_ = -1; private: std::string eventprefix() const; std::string eventname() const; void resolve_offset_kprobe(); bool resolve_offset_uprobe(bool safe_mode, bool has_multiple_aps); void attach_multi_kprobe(void); void attach_multi_uprobe(int pid); void attach_kprobe(); void attach_uprobe(int pid, bool safe_mode); // Note: the following usdt attachment functions will only activate a // semaphore if one exists. // // Increment semaphore count manually with memory hogging API (least // preferable) int usdt_sem_up_manual(const std::string &fn_name, void *ctx); // Increment semaphore count manually with BCC addsem API int usdt_sem_up_manual_addsem(int pid, const std::string &fn_name, void *ctx); int usdt_sem_up(BPFfeature &feature, int pid, const std::string &fn_name, void *ctx); void attach_usdt(int pid, BPFfeature &feature); void attach_tracepoint(); void attach_profile(); void attach_interval(); void attach_software(); void attach_hardware(); void attach_watchpoint(int pid, const std::string &mode); void attach_fentry(void); int detach_fentry(void); void attach_iter(void); int detach_iter(void); void attach_raw_tracepoint(void); int detach_raw_tracepoint(void); static std::map cached_prog_fds_; bool use_cached_progfd(BPFfeature &feature); void cache_progfd(void); Probe &probe_; std::vector perf_event_fds_; bool close_progfd_ = true; int progfd_ = -1; uint64_t offset_ = 0; int tracing_fd_ = -1; std::function usdt_destructor_; USDTHelper usdt_helper; BPFtrace &bpftrace_; }; class HelperVerifierError : public std::runtime_error { public: HelperVerifierError(const std::string &msg, libbpf::bpf_func_id func_id_) : std::runtime_error(msg), func_id(func_id_) { } const libbpf::bpf_func_id func_id; }; } // namespace bpftrace bpftrace-0.23.2/src/bfd-disasm.cpp000066400000000000000000000057341477746507000167220ustar00rootroot00000000000000#include #include #include #include #include // bfd.h assumes everyone is using autotools and will error out unless // PACKAGE is defined. Some distros patch this check out. #define PACKAGE "bpftrace" #include "bfd-disasm.h" #include "utils.h" #include #include namespace bpftrace { BfdDisasm::BfdDisasm(std::string &path) : size_(0) { fd_ = open(path.c_str(), O_RDONLY); if (fd_ >= 0) { std::error_code ec; std::filesystem::path fs_path{ path }; std::uintmax_t file_size = std::filesystem::file_size(fs_path, ec); if (file_size != static_cast(-1)) { size_ = file_size; } } } BfdDisasm::~BfdDisasm() { if (fd_ >= 0) close(fd_); } static int fprintf_nop(void *out __attribute__((unused)), const char *fmt __attribute__((unused)), ...) { return 0; } #ifdef LIBBFD_INIT_DISASM_INFO_FOUR_ARGS_SIGNATURE static int fprintf_styled_nop(void *out __attribute__((unused)), enum disassembler_style s __attribute__((unused)), const char *fmt __attribute__((unused)), ...) { return 0; } #endif static AlignState is_aligned_buf(void *buf, uint64_t size, uint64_t offset) { disassembler_ftype disassemble; struct disassemble_info info; std::string tpath = get_pid_exe("self"); bfd *bfdf; bfdf = bfd_openr(tpath.c_str(), nullptr); if (bfdf == nullptr) return AlignState::Fail; if (!bfd_check_format(bfdf, bfd_object)) { bfd_close(bfdf); return AlignState::Fail; } #ifdef LIBBFD_INIT_DISASM_INFO_FOUR_ARGS_SIGNATURE init_disassemble_info(&info, stdout, fprintf_nop, fprintf_styled_nop); #else init_disassemble_info(&info, stdout, fprintf_nop); #endif info.arch = bfd_get_arch(bfdf); info.mach = bfd_get_mach(bfdf); info.buffer = static_cast(buf); info.buffer_length = size; disassemble_init_for_target(&info); #ifdef LIBBFD_DISASM_FOUR_ARGS_SIGNATURE disassemble = disassembler(info.arch, bfd_big_endian(bfdf), info.mach, bfdf); #else disassemble = disassembler(bfdf); #endif uint64_t pc = 0; int count; do { count = disassemble(pc, &info); pc += static_cast(count); if (pc == offset) { bfd_close(bfdf); return AlignState::Ok; } } while (static_cast(count) > 0 && pc < size && pc < offset); bfd_close(bfdf); return AlignState::NotAlign; } AlignState BfdDisasm::is_aligned(uint64_t offset, uint64_t pc) { AlignState aligned = AlignState::Fail; // 100 bytes should be enough to cover next instruction behind pc uint64_t size = std::min(pc + 100, size_); auto buf = std::make_unique(size); if (fd_ < 0) return aligned; uint64_t sz = pread(fd_, buf.get(), size, offset); if (sz == size) aligned = is_aligned_buf(buf.get(), size, pc); else perror("pread failed"); return aligned; } } // namespace bpftrace bpftrace-0.23.2/src/bfd-disasm.h000066400000000000000000000004241477746507000163560ustar00rootroot00000000000000#pragma once #include "disasm.h" namespace bpftrace { class BfdDisasm : public IDisasm { public: BfdDisasm(std::string &path); ~BfdDisasm(); AlignState is_aligned(uint64_t offset, uint64_t pc); private: int fd_ = -1; uint64_t size_; }; } // namespace bpftrace bpftrace-0.23.2/src/bpf_assembler.h000066400000000000000000000103011477746507000171440ustar00rootroot00000000000000// BPF macro assembler copied from bcc/libbpf.h #include // clang-format off /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ #define BPF_ALU64_REG(OP, DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) #define BPF_ALU32_REG(OP, DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) /* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */ #define BPF_ALU64_IMM(OP, DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) #define BPF_ALU32_IMM(OP, DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) /* Short form of mov, dst_reg = src_reg */ #define BPF_MOV64_REG(DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_MOV | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) /* Short form of mov, dst_reg = imm32 */ #define BPF_MOV64_IMM(DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_MOV | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) /* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ #define BPF_LD_IMM64(DST, IMM) \ BPF_LD_IMM64_RAW(DST, 0, IMM) #define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ ((struct bpf_insn) { \ .code = BPF_LD | BPF_DW | BPF_IMM, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = (__s32) (IMM) }), \ ((struct bpf_insn) { \ .code = 0, /* zero is reserved opcode */ \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = (__s32)(((__u64) (IMM)) >> 32) }) #define BPF_PSEUDO_MAP_FD 1 /* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ #define BPF_LD_MAP_FD(DST, MAP_FD) \ BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) /* Direct packet access, R0 = *(uint *) (skb->data + imm32) */ #define BPF_LD_ABS(SIZE, IMM) \ ((struct bpf_insn) { \ .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) /* Memory load, dst_reg = *(uint *) (src_reg + off16) */ #define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* Memory store, *(uint *) (dst_reg + off16) = src_reg */ #define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* Memory store, *(uint *) (dst_reg + off16) = imm32 */ #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ ((struct bpf_insn) { \ .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = 0, \ .off = OFF, \ .imm = IMM }) /* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */ #define BPF_JMP_REG(OP, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */ #define BPF_JMP_IMM(OP, DST, IMM, OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = OFF, \ .imm = IMM }) /* Raw code statement block */ #define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ ((struct bpf_insn) { \ .code = CODE, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = IMM }) /* Program exit */ #define BPF_EXIT_INSN() \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_EXIT, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = 0 }) // clang-format on bpftrace-0.23.2/src/bpfbytecode.cpp000066400000000000000000000255401477746507000171740ustar00rootroot00000000000000#include "bpfbytecode.h" #include "bpftrace.h" #include "globalvars.h" #include "log.h" #include "utils.h" #include #include #include #include namespace bpftrace { BpfBytecode::BpfBytecode(std::span elf) : BpfBytecode(std::as_bytes(elf)) { } BpfBytecode::BpfBytecode(std::span elf) : BpfBytecode(std::as_bytes(elf)) { } static std::optional get_global_var_section_name( std::string_view map_name, const std::unordered_set §ion_names) { for (const auto §ion_name : section_names) { // there are some random chars in the beginning of the map name if (map_name.npos != map_name.find(section_name)) return section_name; } return std::nullopt; } BpfBytecode::BpfBytecode(std::span elf) { int log_level = 0; // In debug mode, show full verifier log. // In verbose mode, only show verifier log for failures. if (bt_debug.find(DebugStage::Verifier) != bt_debug.end()) log_level = 15; else if (bt_verbose) log_level = 1; BPFTRACE_LIBBPF_OPTS(bpf_object_open_opts, opts, .kernel_log_level = static_cast<__u32>(log_level)); bpf_object_ = std::unique_ptr( bpf_object__open_mem(elf.data(), elf.size(), &opts)); if (!bpf_object_) LOG(BUG) << "The produced ELF is not a valid BPF object"; const auto section_names = globalvars::get_section_names(); // Discover maps struct bpf_map *m; bpf_map__for_each (m, bpf_object_.get()) { std::string_view name = bpf_map__name(m); if (auto global_var_section_name_opt = get_global_var_section_name( name, section_names)) { section_names_to_global_vars_map_[std::move( *global_var_section_name_opt)] = m; continue; } maps_.emplace(bpftrace_map_name(bpf_map__name(m)), m); } // Discover programs struct bpf_program *p; bpf_object__for_each_program (p, bpf_object_.get()) { programs_.emplace(bpf_program__name(p), BpfProgram(p)); } } const BpfProgram &BpfBytecode::getProgramForProbe(const Probe &probe) const { auto usdt_location_idx = (probe.type == ProbeType::usdt) ? std::make_optional( probe.usdt_location_idx) : std::nullopt; auto prog = programs_.find( get_function_name_for_probe(probe.name, probe.index, usdt_location_idx)); if (prog == programs_.end()) { prog = programs_.find(get_function_name_for_probe(probe.orig_name, probe.index, usdt_location_idx)); } if (prog == programs_.end()) { std::stringstream msg; if (probe.name != probe.orig_name) msg << "Code not generated for probe: " << probe.name << " from: " << probe.orig_name; else msg << "Code not generated for probe: " << probe.name; throw std::runtime_error(msg.str()); } return prog->second; } BpfProgram &BpfBytecode::getProgramForProbe(const Probe &probe) { return const_cast( const_cast(this)->getProgramForProbe(probe)); } void BpfBytecode::update_global_vars(BPFtrace &bpftrace) { globalvars::update_global_vars(bpf_object_.get(), section_names_to_global_vars_map_, bpftrace); } namespace { // Searches the verifier's log for err_pattern. If a match is found, extracts // the name and ID of the problematic helper and throws a HelperVerifierError. // // Example verfier log extract: // [...] // 36: (b7) r3 = 64 ; R3_w=64 // 37: (85) call bpf_d_path#147 // helper call is not allowed in probe // [...] // // In the above log, "bpf_d_path" is the helper's name and "147" is the ID. void maybe_throw_helper_verifier_error(std::string_view log, std::string_view err_pattern, const std::string &exception_msg_suffix) { auto err_pos = log.find(err_pattern); if (err_pos == log.npos) return; std::string_view call_pattern = " call "; auto call_pos = log.rfind(call_pattern, err_pos); if (call_pos == log.npos) return; auto helper_begin = call_pos + call_pattern.size(); auto hash_pos = log.find("#", helper_begin); if (hash_pos == log.npos) return; auto eol = log.find("\n", hash_pos + 1); if (eol == log.npos) return; auto helper_name = std::string{ log.substr(helper_begin, hash_pos - helper_begin) }; auto func_id = std::stoi( std::string{ log.substr(hash_pos + 1, eol - hash_pos - 1) }); std::string msg = std::string{ "helper " } + helper_name + exception_msg_suffix; throw HelperVerifierError(msg, static_cast(func_id)); } // The log should end with line: // processed N insns (limit 1000000) ... // so we try to find it. If it's not there, it's very likely that the log has // been trimmed due to insufficient log limit. This function checks if that // happened. bool is_log_trimmed(std::string_view log) { static const std::vector tokens = { "processed", "insns" }; return !wildcard_match(log, tokens, true, true); } } // namespace void BpfBytecode::load_progs(const RequiredResources &resources, const BTF &btf, BPFfeature &feature, const Config &config) { std::unordered_map> log_bufs; for (auto &[name, prog] : programs_) { log_bufs[name] = std::vector(config.get(ConfigKeyInt::log_size), '\0'); auto &log_buf = log_bufs[name]; bpf_program__set_log_buf(prog.bpf_prog(), log_buf.data(), log_buf.size()); } std::vector special_probes; for (auto probe : resources.special_probes) special_probes.push_back(probe.second); prepare_progs(special_probes, btf, feature, config); prepare_progs(resources.probes, btf, feature, config); prepare_progs(resources.watchpoint_probes, btf, feature, config); int res = bpf_object__load(bpf_object_.get()); // If requested, print the entire verifier logs, even if loading succeeded. for (const auto &[name, prog] : programs_) { if (bt_debug.find(DebugStage::Verifier) != bt_debug.end()) { std::cout << "BPF verifier log for " << name << ":\n"; std::cout << "--------------------------------------\n"; std::cout << log_bufs[name].data() << std::endl; } } if (res == 0) return; // If loading of bpf_object failed, we try to give user some hints of what // could've gone wrong. std::ostringstream err; for (const auto &[name, prog] : programs_) { if (res == 0 || prog.fd() >= 0) continue; // Unfortunately, a negative fd does not mean that this specific program // caused the failure. It can mean that libbpf didn't even try to load it // b/c some other program failed to load. So, we only log program load // failures when the verifier log is non-empty. std::string_view log(log_bufs[name].data()); if (!log.empty()) { // These should be the only errors that may occur here which do not imply // a bpftrace bug so throw immediately with a proper error message. maybe_throw_helper_verifier_error(log, "helper call is not allowed in probe", " not allowed in probe"); maybe_throw_helper_verifier_error( log, "pointer arithmetic on ptr_or_null_ prohibited, null-check it first", ": result needs to be null-checked before accessing fields"); std::stringstream errmsg; errmsg << "Error loading BPF program for " << name << "."; if (bt_verbose) { errmsg << std::endl << "Kernel error log: " << std::endl << log << std::endl; if (is_log_trimmed(log)) { LOG(WARNING, errmsg) << "Kernel log seems to be trimmed. This may be due to buffer " "not being big enough, try increasing the BPFTRACE_LOG_SIZE " "environment variable beyond the current value of " << log_bufs[name].size() << " bytes"; } } else { errmsg << " Use -v for full kernel error log."; } LOG(ERROR, err) << errmsg.str(); } } if (err.str().empty()) { // The problem does not seem to be in program loading. It may be something // else (e.g. maps failing to load) but we're not able to figure out what // it is so advise user to check libbf output which should contain more // information. LOG(ERROR, err) << "Unknown BPF object load failure. Try using the \"-d libbpf\" " "option to see the full loading log."; } std::cerr << err.str(); throw FatalUserException("Loading BPF object(s) failed."); } void BpfBytecode::prepare_progs(const std::vector &probes, const BTF &btf, BPFfeature &feature, const Config &config) { for (auto &probe : probes) { auto &program = getProgramForProbe(probe); program.set_prog_type(probe); program.set_expected_attach_type(probe, feature); program.set_attach_target(probe, btf, config); program.set_no_autoattach(); } } bool BpfBytecode::all_progs_loaded() { for (const auto &prog : programs_) { if (prog.second.fd() < 0) return false; } return true; } bool BpfBytecode::hasMap(MapType internal_type) const { return maps_.find(to_string(internal_type)) != maps_.end(); } bool BpfBytecode::hasMap(const StackType &stack_type) const { return maps_.find(stack_type.name()) != maps_.end(); } const BpfMap &BpfBytecode::getMap(const std::string &name) const { auto map = maps_.find(name); if (map == maps_.end()) { LOG(BUG) << "Unknown map: " << name; } return map->second; } const BpfMap &BpfBytecode::getMap(MapType internal_type) const { return getMap(to_string(internal_type)); } const BpfMap &BpfBytecode::getMap(int map_id) const { auto map = maps_by_id_.find(map_id); if (map == maps_by_id_.end()) { LOG(BUG) << "Unknown map id: " << std::to_string(map_id); } return *map->second; } const std::map &BpfBytecode::maps() const { return maps_; } int BpfBytecode::countStackMaps() const { int n = 0; for (auto &map : maps_) { if (map.second.is_stack_map()) n++; } return n; } void BpfBytecode::set_map_ids(RequiredResources &resources) { for (auto &map : maps_) { auto map_info = resources.maps_info.find(map.first); if (map_info != resources.maps_info.end() && map_info->second.id != -1) maps_by_id_.emplace(map_info->second.id, &map.second); } } } // namespace bpftrace bpftrace-0.23.2/src/bpfbytecode.h000066400000000000000000000046211477746507000166360ustar00rootroot00000000000000#pragma once #include "bpffeature.h" #include "bpfmap.h" #include "bpfprogram.h" #include "config.h" #include "required_resources.h" #include "types.h" #include #include #include #include #include #include namespace bpftrace { // Representation of the entire BPF bytecode generated by bpftrace. // Created from ELF emitted by CodegenLLVM. // Encapsulates libbpf's 'struct bpf_object' and contains BPF maps and programs. class BpfBytecode { public: BpfBytecode() { } BpfBytecode(std::span elf); BpfBytecode(std::span elf); BpfBytecode(std::span elf); BpfBytecode(const BpfBytecode &) = delete; BpfBytecode &operator=(const BpfBytecode &) = delete; BpfBytecode(BpfBytecode &&) = default; BpfBytecode &operator=(BpfBytecode &&) = default; void update_global_vars(BPFtrace &bpftrace); void load_progs(const RequiredResources &resources, const BTF &btf, BPFfeature &feature, const Config &config); const BpfProgram &getProgramForProbe(const Probe &probe) const; BpfProgram &getProgramForProbe(const Probe &probe); bool hasMap(MapType internal_type) const; bool hasMap(const StackType &stack_type) const; const BpfMap &getMap(const std::string &name) const; const BpfMap &getMap(MapType internal_type) const; const BpfMap &getMap(int map_id) const; void set_map_ids(RequiredResources &resources); const std::map &maps() const; int countStackMaps() const; private: void prepare_progs(const std::vector &probes, const BTF &btf, BPFfeature &feature, const Config &config); bool all_progs_loaded(); // We need a custom deleter for bpf_object which will call bpf_object__close. // Note that it is not possible to run bpf_object__close in ~BpfBytecode // as the desctuctor may be called upon move assignment. struct bpf_object_deleter { void operator()(struct bpf_object *object) { bpf_object__close(object); } }; std::unique_ptr bpf_object_; std::map maps_; std::map maps_by_id_; std::map programs_; std::unordered_map section_names_to_global_vars_map_; }; } // namespace bpftrace bpftrace-0.23.2/src/bpffeature.cpp000066400000000000000000000506461477746507000170360ustar00rootroot00000000000000#include "bpffeature.h" #include #include #include #include #include #include #include #include #include #include #include "bpf_assembler.h" #include "btf.h" #include "debugfs.h" #include "dwarf_parser.h" #include "probe_matcher.h" #include "tracefs.h" #include "utils.h" namespace bpftrace { int BPFnofeature::parse(const char* str) { for (auto feat : split_string(str, ',')) { if (feat == "kprobe_multi") { kprobe_multi_ = true; } else if (feat == "uprobe_multi") { uprobe_multi_ = true; } else { return -1; } } return 0; } static bool try_load_(const char* name, enum libbpf::bpf_prog_type prog_type, std::optional attach_type, std::optional attach_btf_id, struct bpf_insn* insns, size_t insns_cnt, int loglevel, char* logbuf, size_t logbuf_size, int* outfd = nullptr) { const KernelVersionMethod methods[] = { vDSO, UTS, File }; for (KernelVersionMethod method : methods) { auto version = kernel_version(method); if (method != vDSO && !version) { // Recent kernels don't check the version so we should try to call // bpf_prog_load during first iteration even if we failed to determine // the version. We should not do that in subsequent iterations to avoid // zeroing of log_buf on systems with older kernels. continue; } BPFTRACE_LIBBPF_OPTS(bpf_prog_load_opts, opts); opts.log_buf = logbuf; opts.log_size = logbuf_size; opts.log_level = loglevel; opts.kern_version = version; if (attach_type.has_value()) { opts.expected_attach_type = static_cast<::bpf_attach_type>( attach_type.value()); } if (attach_btf_id.has_value()) opts.attach_btf_id = attach_btf_id.value(); int ret = bpf_prog_load(static_cast<::bpf_prog_type>(prog_type), name, "GPL", insns, insns_cnt, &opts); if (ret >= 0) { if (outfd) *outfd = ret; else close(ret); return true; } } return false; } bool BPFfeature::try_load(enum libbpf::bpf_prog_type prog_type, struct bpf_insn* insns, size_t len, const char* name, std::optional attach_type, int* outfd) { constexpr int log_size = 4096; char logbuf[log_size] = {}; std::optional btf_id; if (prog_type == libbpf::BPF_PROG_TYPE_TRACING && has_btf()) { btf_id = btf_.get_btf_id(name, "vmlinux"); } if (prog_type == libbpf::BPF_PROG_TYPE_TRACING) { // List of available functions must be readable std::ifstream traceable_funcs(tracefs::available_filter_functions()); if (!traceable_funcs.good()) return false; } return try_load_(name, prog_type, attach_type, btf_id, insns, len, 0, logbuf, log_size, outfd); } bool BPFfeature::try_load_btf(const void* btf_data, size_t btf_size) { constexpr int log_size = 4096; char log_buf[log_size] = {}; BPFTRACE_LIBBPF_OPTS(bpf_btf_load_opts, btf_opts, .log_buf = log_buf, .log_level = 0, .log_size = log_size, ); int fd = bpf_btf_load(btf_data, btf_size, &btf_opts); if (fd >= 0) { close(fd); return true; } return false; } bool BPFfeature::detect_helper(enum libbpf::bpf_func_id func_id, enum libbpf::bpf_prog_type prog_type) { // Stolen from libbpf's bpf_probe_helper char logbuf[4096] = {}; char* buf = logbuf; struct bpf_insn insns[] = { BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, func_id), BPF_EXIT_INSN(), }; if (try_load_(nullptr, prog_type, std::nullopt, std::nullopt, insns, ARRAY_SIZE(insns), 1, logbuf, 4096)) return true; if (errno == EPERM) return false; // On older kernels the first byte can be zero, skip leading 0 bytes // $2 = "\000: (85) call 4\nR1 type=ctx expected=fp\n", '\000' // ^^ for (int i = 0; i < 8 && *buf == 0; i++, buf++) ; if (*buf == 0) return false; return (strstr(buf, "invalid func ") == nullptr) && (strstr(buf, "unknown func ") == nullptr) && (strstr(buf, "program of this type cannot use helper ") == nullptr); } bool BPFfeature::detect_prog_type( enum libbpf::bpf_prog_type prog_type, const char* name, std::optional attach_type, int* outfd) { struct bpf_insn insns[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN() }; return try_load( prog_type, insns, ARRAY_SIZE(insns), name, attach_type, outfd); } bool BPFfeature::detect_map(enum libbpf::bpf_map_type map_type) { int key_size = 4; int value_size = 4; int max_entries = 1; int flags = 0; int map_fd = 0; switch (map_type) { case libbpf::BPF_MAP_TYPE_STACK_TRACE: value_size = 8; break; case libbpf::BPF_MAP_TYPE_RINGBUF: // values from libbpf/src/libbpf_probes.c // default pagesize 4KB // default perf_rb_pages 64 key_size = 0; value_size = 0; max_entries = sysconf(_SC_PAGE_SIZE); break; default: break; } BPFTRACE_LIBBPF_OPTS(bpf_map_create_opts, opts); opts.map_flags = flags; map_fd = bpf_map_create(static_cast(map_type), nullptr, key_size, value_size, max_entries, &opts); if (map_fd >= 0) close(map_fd); return map_fd >= 0; } bool BPFfeature::has_btf() { return btf_.has_data(); } bool BPFfeature::has_btf_func_global() { if (has_btf_func_global_.has_value()) return *has_btf_func_global_; /* static void x(int a) {} */ __u32 types[] = { /* int */ BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ /* FUNC_PROTO */ /* [2] */ BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 0), BTF_PARAM_ENC(7, 1), /* FUNC x BTF_FUNC_GLOBAL */ /* [3] */ BTF_TYPE_ENC(5, BTF_INFO_ENC(BTF_KIND_FUNC, 0, BTF_FUNC_GLOBAL), 2), }; has_btf_func_global_ = std::make_optional( try_load_btf(types, sizeof(types))); return *has_btf_func_global_; } int BPFfeature::instruction_limit() { if (insns_limit_.has_value()) return *insns_limit_; struct bpf_insn insns[] = { BPF_LD_IMM64(BPF_REG_0, 0), BPF_EXIT_INSN(), }; constexpr int logsize = 4096; char logbuf[logsize] = {}; bool res = try_load_(nullptr, libbpf::BPF_PROG_TYPE_KPROBE, std::nullopt, std::nullopt, insns, ARRAY_SIZE(insns), 1, logbuf, logsize); if (!res) insns_limit_ = std::make_optional(-1); // Extract limit from the verifier log: // processed 2 insns (limit 131072), stack depth 0 std::string log(logbuf, logsize); std::size_t line_start = log.find("processed 2 insns"); if (line_start == std::string::npos) { insns_limit_ = std::make_optional(-1); return *insns_limit_; } // Old kernels don't have the instruction limit in the verifier output auto begin = log.find("limit", line_start); if (begin == std::string::npos) { insns_limit_ = std::make_optional(-1); return *insns_limit_; } begin += 6; /* "limit " = 6*/ std::size_t end = log.find(")", begin); std::string cnt = log.substr(begin, end - begin); insns_limit_ = std::make_optional(std::stoi(cnt)); return *insns_limit_; } bool BPFfeature::has_map_batch() { int key_size = 4; int value_size = 4; int max_entries = 10; int flags = 0; int map_fd = 0; int keys[10]; int values[10]; uint32_t count = 0; if (has_map_batch_.has_value()) return *has_map_batch_; BPFTRACE_LIBBPF_OPTS(bpf_map_create_opts, opts); opts.map_flags = flags; map_fd = bpf_map_create(static_cast( libbpf::BPF_MAP_TYPE_HASH), nullptr, key_size, value_size, max_entries, &opts); if (map_fd < 0) return false; int err = bpf_map_lookup_batch( map_fd, nullptr, nullptr, keys, values, &count, nullptr); close(map_fd); has_map_batch_ = err >= 0; return *has_map_batch_; } bool BPFfeature::has_d_path() { if (has_d_path_.has_value()) return *has_d_path_; struct bpf_insn insns[] = { BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_MOV64_IMM(BPF_REG_6, 0), BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, 0), BPF_LD_IMM64(BPF_REG_3, 8), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, libbpf::BPF_FUNC_d_path), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }; has_d_path_ = std::make_optional(try_load(libbpf::BPF_PROG_TYPE_TRACING, insns, ARRAY_SIZE(insns), "dentry_open", libbpf::BPF_TRACE_FENTRY)); return *has_d_path_; } bool BPFfeature::has_uprobe_refcnt() { if (has_uprobe_refcnt_.has_value()) return *has_uprobe_refcnt_; std::error_code ec; std::filesystem::path path{ "/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset" }; has_uprobe_refcnt_ = std::filesystem::exists(path, ec); return *has_uprobe_refcnt_; } bool BPFfeature::has_kprobe_multi() { if (has_kprobe_multi_.has_value()) return *has_kprobe_multi_; if (no_feature_.kprobe_multi_) { has_kprobe_multi_ = false; return *has_kprobe_multi_; } const char* sym = "ksys_read"; BPFTRACE_LIBBPF_OPTS(bpf_link_create_opts, link_opts); int progfd, linkfd = -1; struct bpf_insn insns[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }; link_opts.kprobe_multi.syms = &sym; link_opts.kprobe_multi.cnt = 1; BPFTRACE_LIBBPF_OPTS(bpf_prog_load_opts, load_opts); load_opts.expected_attach_type = static_cast( libbpf::BPF_TRACE_KPROBE_MULTI); progfd = bpf_prog_load(static_cast<::bpf_prog_type>( libbpf::BPF_PROG_TYPE_KPROBE), sym, "GPL", reinterpret_cast(insns), ARRAY_SIZE(insns), &load_opts); if (progfd >= 0) { linkfd = bpf_link_create(progfd, 0, static_cast( libbpf::BPF_TRACE_KPROBE_MULTI), &link_opts); } has_kprobe_multi_ = linkfd >= 0; if (linkfd >= 0) { close(linkfd); } if (progfd >= 0) { close(progfd); } return *has_kprobe_multi_; } bool BPFfeature::has_uprobe_multi() { if (has_uprobe_multi_.has_value()) return *has_uprobe_multi_; #if defined(HAVE_LIBBPF_UPROBE_MULTI) if (no_feature_.uprobe_multi_) { has_uprobe_multi_ = false; return *has_uprobe_multi_; } BPFTRACE_LIBBPF_OPTS( bpf_prog_load_opts, load_opts, .expected_attach_type = static_cast( libbpf::BPF_TRACE_UPROBE_MULTI), ); int err = 0, progfd, linkfd = -1; struct bpf_insn insns[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }; progfd = bpf_prog_load(static_cast<::bpf_prog_type>( libbpf::BPF_PROG_TYPE_KPROBE), "uprobe_multi", "GPL", reinterpret_cast(insns), ARRAY_SIZE(insns), &load_opts); if (progfd >= 0) { BPFTRACE_LIBBPF_OPTS(bpf_link_create_opts, link_opts); const unsigned long offset = 0; link_opts.uprobe_multi.path = "/"; link_opts.uprobe_multi.offsets = &offset; link_opts.uprobe_multi.cnt = 1; linkfd = bpf_link_create(progfd, 0, static_cast( libbpf::BPF_TRACE_UPROBE_MULTI), &link_opts); err = -errno; } has_uprobe_multi_ = linkfd < 0 && err == -EBADF; if (linkfd >= 0) { close(linkfd); } if (progfd >= 0) { close(progfd); } #else has_uprobe_multi_ = false; #endif // HAVE_LIBBPF_UPROBE_MULTI return *has_uprobe_multi_; // NOLINT(bugprone-unchecked-optional-access) } bool BPFfeature::has_skb_output() { if (!has_fentry()) return false; if (has_skb_output_.has_value()) return *has_skb_output_; int map_fd = 0; BPFTRACE_LIBBPF_OPTS(bpf_map_create_opts, opts); opts.map_flags = 0; map_fd = bpf_map_create(static_cast( libbpf::BPF_MAP_TYPE_PERF_EVENT_ARRAY), "rb", sizeof(int), sizeof(int), 1, &opts); if (map_fd < 0) return false; struct bpf_insn insns[] = { BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0), BPF_LD_MAP_FD(BPF_REG_2, map_fd), BPF_MOV64_IMM(BPF_REG_3, 0), BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), BPF_MOV64_IMM(BPF_REG_6, 0), BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_6, 0), BPF_LD_IMM64(BPF_REG_5, 8), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, libbpf::BPF_FUNC_skb_output), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }; has_skb_output_ = std::make_optional( try_load(libbpf::BPF_PROG_TYPE_TRACING, insns, ARRAY_SIZE(insns), "__kfree_skb", libbpf::BPF_TRACE_FENTRY)); close(map_fd); return *has_skb_output_; } static void tabulate(std::stringstream& buf, std::vector>& data) { size_t len = data.size(); constexpr int width = 35; for (size_t i = 0; i < len; i += 2) { buf << std::setw(width) << std::left << " " + data[i].first + ": " + data[i].second << std::setw(width); if (i + 1 < len) { buf << data[i + 1].first + ": " + data[i + 1].second << std::endl; } else { buf << std::endl; } } } std::string BPFfeature::report() { std::stringstream buf; auto to_str = [](bool f) -> std::string { return f ? "yes" : "no"; }; std::vector> helpers = { { "probe_read", to_str(has_helper_probe_read()) }, { "probe_read_str", to_str(has_helper_probe_read_str()) }, { "probe_read_user", to_str(has_helper_probe_read_user()) }, { "probe_read_user_str", to_str(has_helper_probe_read_user_str()) }, { "probe_read_kernel", to_str(has_helper_probe_read_kernel()) }, { "probe_read_kernel_str", to_str(has_helper_probe_read_kernel_str()) }, { "get_current_cgroup_id", to_str(has_helper_get_current_cgroup_id()) }, { "send_signal", to_str(has_helper_send_signal()) }, { "override_return", to_str(has_helper_override_return()) }, { "get_boot_ns", to_str(has_helper_ktime_get_boot_ns()) }, { "dpath", to_str(has_d_path()) }, { "skboutput", to_str(has_skb_output()) }, { "get_tai_ns", to_str(has_helper_ktime_get_tai_ns()) }, { "get_func_ip", to_str(has_helper_get_func_ip()) }, { "jiffies64", to_str(has_helper_jiffies64()) }, { "for_each_map_elem", to_str(has_helper_for_each_map_elem()) }, { "get_ns_current_pid_tgid", to_str(has_helper_get_ns_current_pid_tgid()) }, { "lookup_percpu_elem", to_str(has_helper_map_lookup_percpu_elem()) }, }; std::vector> features = { { "Instruction limit", std::to_string(instruction_limit()) }, { "btf", to_str(has_btf()) }, { "module btf", to_str(has_module_btf()) }, { "Kernel DWARF", to_str(has_kernel_dwarf()) }, { "map batch", to_str(has_map_batch()) }, // Depends on BCC's bpf_attach_uprobe refcount feature { "uprobe refcount", to_str(has_uprobe_refcnt()) } }; std::vector> map_types = { { "hash", to_str(has_map_hash()) }, { "array", to_str(has_map_array()) }, { "percpu array", to_str(has_map_percpu_array()) }, { "stack_trace", to_str(has_map_stack_trace()) }, { "perf_event_array", to_str(has_map_perf_event_array()) }, { "ringbuf", to_str(has_map_ringbuf()) } }; std::vector> probe_types = { { "kprobe", to_str(has_prog_kprobe()) }, { "tracepoint", to_str(has_prog_tracepoint()) }, { "perf_event", to_str(has_prog_perf_event()) }, { "fentry", to_str(has_fentry()) }, { "kprobe_multi", to_str(has_kprobe_multi()) }, { "uprobe_multi", to_str(has_uprobe_multi()) }, { "iter", to_str(has_iter("task")) } }; buf << "Kernel helpers" << std::endl; tabulate(buf, helpers); buf << std::endl; buf << "Kernel features" << std::endl; tabulate(buf, features); buf << std::endl; buf << "Map types" << std::endl; tabulate(buf, map_types); buf << std::endl; buf << "Probe types" << std::endl; tabulate(buf, probe_types); buf << std::endl; return buf.str(); } bool BPFfeature::has_prog_fentry() { if (!has_prog_fentry_.has_value()) { int progfd; if (!detect_prog_type(libbpf::BPF_PROG_TYPE_TRACING, "sched_fork", libbpf::BPF_TRACE_FENTRY, &progfd)) goto out_false; int tracing_fd = bpf_raw_tracepoint_open(nullptr, progfd); close(progfd); if (tracing_fd < 0) goto out_false; close(tracing_fd); has_prog_fentry_ = std::make_optional(true); } return *(has_prog_fentry_); out_false: has_prog_fentry_ = std::make_optional(false); return *(has_prog_fentry_); } bool BPFfeature::has_fentry() { return has_prog_fentry() && btf_.has_data(); } bool BPFfeature::has_module_btf() { if (has_module_btf_.has_value()) return *has_module_btf_; char name[64]; struct bpf_btf_info info = {}; info.name = reinterpret_cast(name); info.name_len = sizeof(name); __u32 id = 0, info_len = sizeof(info); int err = 0, fd = -1; err = bpf_btf_get_next_id(id, &id); if (err) goto not_support; fd = bpf_btf_get_fd_by_id(id); if (fd < 0) goto not_support; err = bpf_obj_get_info_by_fd(fd, &info, &info_len); close(fd); if (err) goto not_support; has_module_btf_ = true; return *has_module_btf_; not_support: has_module_btf_ = false; return *has_module_btf_; } bool BPFfeature::has_iter(std::string name) { auto tracing_name = "bpf_iter_" + name; return detect_prog_type(libbpf::BPF_PROG_TYPE_TRACING, tracing_name.c_str(), libbpf::BPF_TRACE_ITER); } bool BPFfeature::has_kernel_dwarf() { #ifndef HAVE_LIBLLDB return false; #endif auto vmlinux = find_vmlinux(); if (!vmlinux.has_value()) return false; // WARNING: we are not passing a pointer to BPFtrace, so we can only use: // * Dwarf::has_debug_info // * Dwarf::get_function_locations // * Dwarf::get_function_params // Otherwise, Dwarf will try to use the BPFtrace pointer and will segfault. auto dwarf = Dwarf::GetFromBinary(nullptr, vmlinux.value()); if (!dwarf) return false; return dwarf->has_debug_info(); } bool BPFfeature::has_kernel_func(Kfunc kfunc) { if (!has_btf()) return false; auto find_kfunc = available_kernel_funcs_.find(kfunc); if (find_kfunc != available_kernel_funcs_.end()) return find_kfunc->second; bool result = btf_.get_btf_id(kfunc_name(kfunc), "") >= 0; available_kernel_funcs_.emplace(kfunc, result); return result; } } // namespace bpftrace bpftrace-0.23.2/src/bpffeature.h000066400000000000000000000163001477746507000164700ustar00rootroot00000000000000#pragma once #include "btf.h" #include "kfuncs.h" #include #include #include #include namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf namespace bpftrace { #define DEFINE_MAP_TEST(var, maptype) \ protected: \ std::optional map_##var##_; \ \ public: \ bool has_map_##var(void) \ { \ if (!map_##var##_.has_value()) \ map_##var##_ = std::make_optional(detect_map((maptype))); \ return *(map_##var##_); \ } #define DEFINE_HELPER_TEST(name, progtype) \ protected: \ std::optional has_##name##_; \ \ public: \ bool has_helper_##name(void) \ { \ if (!has_##name##_.has_value()) \ has_##name##_ = std::make_optional( \ detect_helper(libbpf::BPF_FUNC_##name, (progtype))); \ return *(has_##name##_); \ } #define __DEFINE_PROG_TEST(var, progtype, name, attach_type) \ protected: \ std::optional prog_##var##_; \ \ public: \ bool has_prog_##var(void) \ { \ if (!prog_##var##_.has_value()) \ prog_##var##_ = std::make_optional( \ detect_prog_type((progtype), (name), (attach_type))); \ return *(prog_##var##_); \ } #define DEFINE_PROG_TEST(var, progtype) \ __DEFINE_PROG_TEST(var, progtype, NULL, std::nullopt) class BPFfeature; class BPFnofeature { public: BPFnofeature() : kprobe_multi_(false), uprobe_multi_(false) { } int parse(const char* optarg); protected: bool kprobe_multi_; bool uprobe_multi_; friend class BPFfeature; }; class BPFfeature { public: BPFfeature(BPFnofeature& no_feature) : no_feature_(no_feature) { } BPFfeature() = default; virtual ~BPFfeature() = default; // Due to the unique_ptr usage the generated copy constructor & assignment // don't work. Move works but doesn't make sense as the `has_*` functions // will just reassign the unique_ptr. // A single bpffeature should be constructed in main() and passed around, // marking these as deleted to avoid accidentally copying/moving it. BPFfeature(const BPFfeature&) = delete; BPFfeature& operator=(const BPFfeature&) = delete; BPFfeature(BPFfeature&&) = delete; BPFfeature& operator=(BPFfeature&&) = delete; int instruction_limit(); bool has_btf(); bool has_btf_func_global(); bool has_map_batch(); bool has_d_path(); bool has_uprobe_refcnt(); bool has_kprobe_multi(); bool has_uprobe_multi(); bool has_fentry(); bool has_skb_output(); bool has_prog_fentry(); bool has_module_btf(); bool has_iter(std::string name); bool has_kernel_dwarf(); bool has_kernel_func(Kfunc kfunc); std::string report(void); DEFINE_MAP_TEST(array, libbpf::BPF_MAP_TYPE_ARRAY); DEFINE_MAP_TEST(hash, libbpf::BPF_MAP_TYPE_HASH); DEFINE_MAP_TEST(percpu_array, libbpf::BPF_MAP_TYPE_PERCPU_ARRAY); DEFINE_MAP_TEST(stack_trace, libbpf::BPF_MAP_TYPE_STACK_TRACE); DEFINE_MAP_TEST(perf_event_array, libbpf::BPF_MAP_TYPE_PERF_EVENT_ARRAY); DEFINE_MAP_TEST(ringbuf, libbpf::BPF_MAP_TYPE_RINGBUF); DEFINE_HELPER_TEST(send_signal, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(override_return, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(get_current_cgroup_id, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(probe_read, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(probe_read_str, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(probe_read_user, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(probe_read_kernel, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(probe_read_user_str, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(probe_read_kernel_str, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(ktime_get_boot_ns, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(ktime_get_tai_ns, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(get_func_ip, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(jiffies64, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(for_each_map_elem, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(get_ns_current_pid_tgid, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_HELPER_TEST(map_lookup_percpu_elem, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_PROG_TEST(kprobe, libbpf::BPF_PROG_TYPE_KPROBE); DEFINE_PROG_TEST(tracepoint, libbpf::BPF_PROG_TYPE_TRACEPOINT); DEFINE_PROG_TEST(perf_event, libbpf::BPF_PROG_TYPE_PERF_EVENT); protected: std::optional has_d_path_; std::optional insns_limit_; std::optional has_map_batch_; std::optional has_uprobe_refcnt_; std::optional has_kprobe_multi_; std::optional has_uprobe_multi_; std::optional has_skb_output_; std::optional has_prog_fentry_; std::optional has_module_btf_; std::optional has_btf_func_global_; std::optional has_kernel_dwarf_; std::unordered_map available_kernel_funcs_; private: bool detect_map(libbpf::bpf_map_type map_type); bool detect_helper(libbpf::bpf_func_id func_id, libbpf::bpf_prog_type prog_type); bool detect_prog_type(libbpf::bpf_prog_type prog_type, const char* name, std::optional attach_type, int* outfd = nullptr); bool try_load( libbpf::bpf_prog_type prog_type, struct bpf_insn* insns, size_t len, const char* name = nullptr, std::optional attach_type = std::nullopt, int* outfd = nullptr); bool try_load_btf(const void* btf_data, size_t btf_size); BTF btf_ = BTF({ "vmlinux" }); BPFnofeature no_feature_; }; #undef DEFINE_PROG_TEST #undef DEFINE_MAP_TEST #undef DEFINE_HELPER_TEST } // namespace bpftrace bpftrace-0.23.2/src/bpfmap.cpp000066400000000000000000000025501477746507000161470ustar00rootroot00000000000000#include "bpfmap.h" namespace bpftrace { int BpfMap::fd() const { return bpf_map__fd(bpf_map_); } libbpf::bpf_map_type BpfMap::type() const { return type_; } cstring_view BpfMap::bpf_name() const { return name_; } std::string BpfMap::name() const { return bpftrace_map_name(bpf_name()); } uint32_t BpfMap::key_size() const { return key_size_; } uint32_t BpfMap::value_size() const { return value_size_; } uint32_t BpfMap::max_entries() const { return max_entries_; } bool BpfMap::is_stack_map() const { return name().compare(0, 6, "stack_") == 0; } bool BpfMap::is_per_cpu_type() const { return type() == libbpf::BPF_MAP_TYPE_PERCPU_HASH || type() == libbpf::BPF_MAP_TYPE_PERCPU_ARRAY; } bool BpfMap::is_clearable() const { return is_bpf_map_clearable(type()); } bool BpfMap::is_printable() const { // Internal maps are not printable return bpf_name().compare(0, 3, "AT_") == 0; } std::string to_string(MapType t) { switch (t) { case MapType::PerfEvent: return "perf_event"; case MapType::Join: return "join"; case MapType::Elapsed: return "elapsed"; case MapType::Ringbuf: return "ringbuf"; case MapType::EventLossCounter: return "event_loss_counter"; case MapType::RecursionPrevention: return "recursion_prevention"; } return {}; // unreached } } // namespace bpftrace bpftrace-0.23.2/src/bpfmap.h000066400000000000000000000043741477746507000156220ustar00rootroot00000000000000#pragma once #include #include #include #include #include namespace libbpf { #include "libbpf/bpf.h" } // namespace libbpf #include "container/cstring_view.h" namespace bpftrace { class BpfMap { public: BpfMap(struct bpf_map *bpf_map) : bpf_map_(bpf_map), type_(static_cast(bpf_map__type(bpf_map))), name_(bpf_map__name(bpf_map)), key_size_(bpf_map__key_size(bpf_map)), value_size_(bpf_map__value_size(bpf_map)), max_entries_(bpf_map__max_entries(bpf_map)) { } BpfMap(libbpf::bpf_map_type type, cstring_view name, uint32_t key_size, uint32_t value_size, uint32_t max_entries) : type_(type), name_(name), key_size_(key_size), value_size_(value_size), max_entries_(max_entries) { } int fd() const; libbpf::bpf_map_type type() const; cstring_view bpf_name() const; std::string name() const; uint32_t key_size() const; uint32_t value_size() const; uint32_t max_entries() const; bool is_stack_map() const; bool is_per_cpu_type() const; bool is_clearable() const; bool is_printable() const; private: struct bpf_map *bpf_map_; libbpf::bpf_map_type type_; cstring_view name_; uint32_t key_size_; uint32_t value_size_; uint32_t max_entries_; }; // Internal map types enum class MapType { // Also update to_string PerfEvent, Join, Elapsed, Ringbuf, EventLossCounter, RecursionPrevention, }; std::string to_string(MapType t); // BPF maps do not accept "@" in name so we replace it by "AT_". // The below two functions do the translations. inline std::string bpf_map_name(std::string_view bpftrace_map_name) { auto name = std::string{ bpftrace_map_name }; if (name[0] == '@') name = "AT_" + name.substr(1); return name; } inline std::string bpftrace_map_name(std::string_view bpf_map_name) { auto name = std::string{ bpf_map_name }; if (name.compare(0, 3, "AT_") == 0) name = "@" + name.substr(3); return name; } inline bool is_bpf_map_clearable(libbpf::bpf_map_type map_type) { return map_type != libbpf::BPF_MAP_TYPE_ARRAY && map_type != libbpf::BPF_MAP_TYPE_PERCPU_ARRAY; } } // namespace bpftrace bpftrace-0.23.2/src/bpfprogram.cpp000066400000000000000000000064341477746507000170460ustar00rootroot00000000000000#include "bpfprogram.h" #include "attached_probe.h" #include "log.h" #include "utils.h" #include #include #include #include #include namespace bpftrace { BpfProgram::BpfProgram(struct bpf_program *bpf_prog) : bpf_prog_(bpf_prog) { } int BpfProgram::fd() const { return bpf_program__fd(bpf_prog_); } void BpfProgram::set_prog_type(const Probe &probe) { auto prog_type = progtype(probe.type); bpf_program__set_type(bpf_prog_, static_cast<::bpf_prog_type>(prog_type)); } void BpfProgram::set_expected_attach_type(const Probe &probe, BPFfeature &feature) { libbpf::bpf_attach_type attach_type = static_cast(0); if (probe.type == ProbeType::fentry) attach_type = libbpf::BPF_TRACE_FENTRY; else if (probe.type == ProbeType::fexit) attach_type = libbpf::BPF_TRACE_FEXIT; else if (probe.type == ProbeType::iter) attach_type = libbpf::BPF_TRACE_ITER; // We want to avoid kprobe_multi when a module is specified // because the BPF_TRACE_KPROBE_MULTI link type does not // currently support the `module:function` syntax. if ((probe.type == ProbeType::kprobe || probe.type == ProbeType::kretprobe) && feature.has_kprobe_multi() && !probe.funcs.empty() && probe.path.empty()) attach_type = libbpf::BPF_TRACE_KPROBE_MULTI; if ((probe.type == ProbeType::uprobe || probe.type == ProbeType::uretprobe) && feature.has_uprobe_multi() && !probe.funcs.empty()) attach_type = libbpf::BPF_TRACE_UPROBE_MULTI; bpf_program__set_expected_attach_type( bpf_prog_, static_cast<::bpf_attach_type>(attach_type)); } void BpfProgram::set_attach_target(const Probe &probe, const BTF &btf, const Config &config) { if (probe.type != ProbeType::fentry && probe.type != ProbeType::fexit && probe.type != ProbeType::iter) return; const std::string &mod = probe.path; const std::string &fun = probe.attach_point; const std::string attach_target = !mod.empty() ? mod + ":" + fun : fun; const std::string &btf_fun = probe.type == ProbeType::iter ? "bpf_iter_" + fun : fun; if (btf.get_btf_id(btf_fun, mod) < 0) { const std::string msg = "No BTF found for " + attach_target; if (probe.orig_name != probe.name && config.get(ConfigKeyMissingProbes::default_) != ConfigMissingProbes::error) { // One attach point in a multi-attachpoint probe failed and the user // requested not to error out. Show a warning (if requested) and continue // but disable auto-loading of the program as it would make the entire BPF // object loading fail. if (config.get(ConfigKeyMissingProbes::default_) == ConfigMissingProbes::warn) LOG(WARNING) << msg << ", skipping."; bpf_program__set_autoload(bpf_prog_, false); } else { // explicit match failed, fail hard throw FatalUserException(msg); } } bpf_program__set_attach_target(bpf_prog_, 0, attach_target.c_str()); } void BpfProgram::set_no_autoattach() { bpf_program__set_autoattach(bpf_prog_, false); } struct bpf_program *BpfProgram::bpf_prog() const { return bpf_prog_; } } // namespace bpftrace bpftrace-0.23.2/src/bpfprogram.h000066400000000000000000000016631477746507000165120ustar00rootroot00000000000000#pragma once #include "bpffeature.h" #include "btf.h" #include "config.h" #include "types.h" #include namespace bpftrace { class BpfBytecode; class BPFtrace; // This class abstracts a single BPF program by encapsulating libbpf's // 'struct bpf_prog'. class BpfProgram { public: explicit BpfProgram(struct bpf_program *bpf_prog); void set_prog_type(const Probe &probe); void set_expected_attach_type(const Probe &probe, BPFfeature &feature); void set_attach_target(const Probe &probe, const BTF &btf, const Config &config); void set_no_autoattach(); int fd() const; struct bpf_program *bpf_prog() const; BpfProgram(const BpfProgram &) = delete; BpfProgram &operator=(const BpfProgram &) = delete; BpfProgram(BpfProgram &&) = default; BpfProgram &operator=(BpfProgram &&) = default; private: struct bpf_program *bpf_prog_; }; } // namespace bpftrace bpftrace-0.23.2/src/bpftrace.cpp000066400000000000000000002007021477746507000164670ustar00rootroot00000000000000#include "btf.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBSYSTEMD #include #endif #include "ast/async_event_types.h" #include "bpfmap.h" #include "bpfprogram.h" #include "bpftrace.h" #include "log.h" #include "printf.h" #include "resolve_cgroupid.h" #include "scopeguard.h" #include "utils.h" namespace bpftrace { std::set bt_debug = {}; bool bt_quiet = false; bool bt_verbose = false; bool dry_run = false; int BPFtrace::exit_code = 0; volatile sig_atomic_t BPFtrace::exitsig_recv = false; volatile sig_atomic_t BPFtrace::sigusr1_recv = false; BPFtrace::~BPFtrace() { close_pcaps(); } Probe BPFtrace::generateWatchpointSetupProbe(const ast::AttachPoint &ap, const ast::Probe &probe) { Probe setup_probe; setup_probe.name = get_watchpoint_setup_probe_name(ap.name()); setup_probe.type = ProbeType::uprobe; setup_probe.path = ap.target; setup_probe.attach_point = ap.func; setup_probe.orig_name = get_watchpoint_setup_probe_name(probe.name()); setup_probe.index = ap.index() > 0 ? ap.index() : probe.index(); return setup_probe; } Probe BPFtrace::generate_probe(const ast::AttachPoint &ap, const ast::Probe &p, int usdt_location_idx) { Probe probe; probe.path = ap.target; probe.attach_point = ap.func; probe.type = probetype(ap.provider); probe.log_size = config_.get(ConfigKeyInt::log_size); probe.orig_name = p.name(); probe.ns = ap.ns; probe.name = ap.name(); probe.need_expansion = p.need_expansion; probe.freq = ap.freq; probe.address = ap.address; probe.func_offset = ap.func_offset; probe.loc = 0; probe.usdt_location_idx = usdt_location_idx; probe.index = ap.index() ?: p.index(); probe.len = ap.len; probe.mode = ap.mode; probe.async = ap.async; probe.pin = ap.pin; return probe; } int BPFtrace::add_probe(ast::ASTContext &ctx, const ast::AttachPoint &ap, const ast::Probe &p, int usdt_location_idx) { auto type = probetype(ap.provider); auto probe = generate_probe(ap, p, usdt_location_idx); // Add the new probe(s) to resources if (ap.provider == "BEGIN" || ap.provider == "END" || ap.provider == "self") { // special probes auto target = ap.target.empty() ? "" : "_" + ap.target; auto name = ap.provider + target; resources.special_probes[name] = std::move(probe); } else if ((type == ProbeType::watchpoint || type == ProbeType::asyncwatchpoint) && ap.func.size()) { // (async)watchpoint - generate also the setup probe resources.probes.emplace_back(generateWatchpointSetupProbe(ap, p)); resources.watchpoint_probes.emplace_back(std::move(probe)); } else if (ap.expansion == ast::ExpansionType::MULTI) { // (k|u)probe_multi - do expansion and set probe.funcs auto matches = probe_matcher_->get_matches_for_ap(ap); if (matches.empty()) return 1; if (has_wildcard(ap.target)) { // If we have a wildcard in the target path, we need to generate one // probe per expanded target. assert(type == ProbeType::uprobe || type == ProbeType::uretprobe); std::unordered_map target_map; for (const auto &func : matches) { ast::AttachPoint &match_ap = ap.create_expansion_copy(ctx, func); // Use the original (possibly wildcarded) function name match_ap.func = ap.func; auto found = target_map.find(match_ap.target); if (found != target_map.end()) { found->second.funcs.push_back(func); } else { auto probe = generate_probe(match_ap, p); probe.funcs.push_back(func); target_map.insert({ { match_ap.target, probe } }); } } for (auto &pair : target_map) { resources.probes.push_back(std::move(pair.second)); } } else { probe.funcs = std::vector(matches.begin(), matches.end()); resources.probes.push_back(std::move(probe)); } } else if (probetype(ap.provider) == ProbeType::uprobe || probetype(ap.provider) == ProbeType::kprobe) { bool locations_from_dwarf = false; // Don't set the DWARF target when the user wants to use the symbol table. std::optional target; if (config_.get(ConfigKeySymbolSource::default_) == ConfigSymbolSource::dwarf) { if (probetype(ap.provider) == ProbeType::uprobe) { target = probe.path; } else { // Only use the DWARF information of the Kernel, // if the user wants to to probe inlined kprobes. // Otherwise, fall back to using the symbol table. if (config_.get(ConfigKeyBool::probe_inline)) target = find_vmlinux(); } } // If the user specified an address/offset, do not overwrite // their choice with locations from the DebugInfo. if (probe.address == 0 && probe.func_offset == 0 && target.has_value()) { // Get function locations from the DebugInfo, as it skips the // prologue and also returns locations of inlined function calls. if (auto *dwarf = get_dwarf(target.value())) { const auto locations = dwarf->get_function_locations( probe.attach_point, config_.get(ConfigKeyBool::probe_inline)); for (const auto loc : locations) { // Clear the attach point, so the address will be used instead Probe probe_copy = probe; probe_copy.attach_point.clear(); probe_copy.address = loc; resources.probes.push_back(std::move(probe_copy)); locations_from_dwarf = true; } } } // Otherwise, use the location from the symbol table. if (!locations_from_dwarf) resources.probes.push_back(std::move(probe)); } else { resources.probes.emplace_back(std::move(probe)); } if (type == ProbeType::iter) has_iter_ = true; // Preload symbol tables if necessary if (resources.probes_using_usym.find(&p) != resources.probes_using_usym.end() && is_exe(ap.target)) { usyms_.cache(ap.target); } return 0; } int BPFtrace::num_probes() const { return resources.special_probes.size() + resources.probes.size(); } void BPFtrace::request_finalize() { finalize_ = true; attached_probes_.clear(); if (child_) child_->terminate(); } void perf_event_printer(void *cb_cookie, void *data, int size) { // The perf event data is not aligned, so we use memcpy to copy the data and // avoid UBSAN errors. Using an std::vector guarantees that it will be aligned // to the largest type. See: // https://stackoverflow.com/questions/8456236/how-is-a-vectors-data-aligned. std::vector data_aligned; data_aligned.resize(size); memcpy(data_aligned.data(), data, size); auto bpftrace = static_cast(cb_cookie); auto arg_data = data_aligned.data(); auto printf_id = *reinterpret_cast(arg_data); int err; // Ignore the remaining events if perf_event_printer is called during // finalization stage (exit() builtin has been called) if (bpftrace->finalize_) return; if (bpftrace->exitsig_recv) { bpftrace->request_finalize(); return; } // async actions if (printf_id == asyncactionint(AsyncAction::exit)) { auto exit = static_cast(data); BPFtrace::exit_code = exit->exit_code; bpftrace->request_finalize(); return; } else if (printf_id == asyncactionint(AsyncAction::print)) { auto print = static_cast(data); auto &map = bpftrace->bytecode_.getMap(print->mapid); err = bpftrace->print_map(map, print->top, print->div); if (err) LOG(BUG) << "Could not print map with ident \"" << map.name() << "\", err=" << std::to_string(err); return; } else if (printf_id == asyncactionint(AsyncAction::print_non_map)) { auto print = static_cast(data); const SizedType &ty = bpftrace->resources.non_map_print_args.at( print->print_id); std::vector bytes; for (size_t i = 0; i < ty.GetSize(); ++i) bytes.emplace_back(reinterpret_cast(print->content[i])); bpftrace->out_->value(*bpftrace, ty, bytes); return; } else if (printf_id == asyncactionint(AsyncAction::clear)) { auto mapevent = static_cast(data); auto &map = bpftrace->bytecode_.getMap(mapevent->mapid); err = bpftrace->clear_map(map); if (err) LOG(BUG) << "Could not clear map with ident \"" << map.name() << "\", err=" << std::to_string(err); return; } else if (printf_id == asyncactionint(AsyncAction::zero)) { auto mapevent = static_cast(data); auto &map = bpftrace->bytecode_.getMap(mapevent->mapid); err = bpftrace->zero_map(map); if (err) LOG(BUG) << "Could not zero map with ident \"" << map.name() << "\", err=" << std::to_string(err); return; } else if (printf_id == asyncactionint(AsyncAction::time)) { char timestr[64]; // not respecting config_.get(ConfigKeyInt::max_strlen) time_t t; struct tm tmp; t = time(nullptr); if (!localtime_r(&t, &tmp)) { LOG(WARNING) << "localtime_r: " << strerror(errno); return; } auto time = static_cast(data); auto fmt = bpftrace->resources.time_args[time->time_id].c_str(); if (strftime(timestr, sizeof(timestr), fmt, &tmp) == 0) { LOG(WARNING) << "strftime returned 0"; return; } bpftrace->out_->message(MessageType::time, timestr, false); return; } else if (printf_id == asyncactionint(AsyncAction::join)) { uint64_t join_id = *(static_cast(data) + 1); auto delim = bpftrace->resources.join_args[join_id].c_str(); std::stringstream joined; for (unsigned int i = 0; i < bpftrace->join_argnum_; i++) { auto *arg = arg_data + 2 * sizeof(uint64_t) + i * bpftrace->join_argsize_; if (arg[0] == 0) break; if (i) joined << delim; joined << arg; } bpftrace->out_->message(MessageType::join, joined.str()); return; } else if (printf_id == asyncactionint(AsyncAction::helper_error)) { auto helpererror = static_cast(data); auto error_id = helpererror->error_id; auto return_value = helpererror->return_value; auto &info = bpftrace->resources.helper_error_info[error_id]; bpftrace->out_->helper_error(info.func_id, return_value, info.loc); return; } else if (printf_id == asyncactionint(AsyncAction::watchpoint_attach)) { bool abort = false; auto watchpoint = static_cast(data); uint64_t probe_idx = watchpoint->watchpoint_idx; uint64_t addr = watchpoint->addr; if (probe_idx >= bpftrace->resources.watchpoint_probes.size()) { LOG(ERROR) << "Invalid watchpoint probe idx=" << probe_idx; abort = true; goto out; } // Ignore duplicate watchpoints (idx && addr same), but allow the same // address to be watched by different probes. // // NB: this check works b/c we set Probe::addr below // // TODO: Should we be printing a warning or info message out here? if (bpftrace->resources.watchpoint_probes[probe_idx].address == addr) goto out; // Attach the real watchpoint probe { bool registers_available = true; Probe &wp_probe = bpftrace->resources.watchpoint_probes[probe_idx]; wp_probe.address = addr; std::vector> aps; try { aps = bpftrace->attach_probe(wp_probe, bpftrace->bytecode_); } catch (const EnospcException &ex) { registers_available = false; bpftrace->out_->message(MessageType::lost_events, "Failed to attach watchpoint probe. You are " "out of watchpoint registers."); goto out; } if (aps.empty() && registers_available) { std::cerr << "Unable to attach real watchpoint probe" << std::endl; abort = true; goto out; } for (auto &ap : aps) bpftrace->attached_probes_.emplace_back(std::move(ap)); } out: // Async watchpoints are not SIGSTOP'd if (bpftrace->resources.watchpoint_probes[probe_idx].async) return; // Let the tracee continue pid_t pid = bpftrace->child_ ? bpftrace->child_->pid() : (bpftrace->procmon_ ? bpftrace->procmon_->pid() : -1); if (pid == -1 || ::kill(pid, SIGCONT) != 0) { std::cerr << "Failed to SIGCONT tracee: " << strerror(errno) << std::endl; abort = true; } if (abort) std::abort(); return; } else if (printf_id == asyncactionint(AsyncAction::watchpoint_detach)) { auto unwatch = static_cast(data); uint64_t addr = unwatch->addr; // Remove all probes watching `addr`. Note how we fail silently here // (ie invalid addr). This lets script writers be a bit more aggressive // when unwatch'ing addresses, especially if they're sampling a portion // of addresses they're interested in watching. bpftrace->attached_probes_.erase( std::remove_if(bpftrace->attached_probes_.begin(), bpftrace->attached_probes_.end(), [&](const auto &ap) { return ap->probe().address == addr; }), bpftrace->attached_probes_.end()); return; } else if (printf_id == asyncactionint(AsyncAction::skboutput)) { struct hdr_t { uint64_t aid; uint64_t id; uint64_t ns; uint8_t pkt[]; } __attribute__((packed)) * hdr; hdr = static_cast(data); int offset = std::get<1>(bpftrace->resources.skboutput_args_.at(hdr->id)); bpftrace->write_pcaps( hdr->id, hdr->ns, hdr->pkt + offset, size - sizeof(*hdr)); return; } else if (printf_id >= asyncactionint(AsyncAction::syscall) && printf_id < asyncactionint(AsyncAction::syscall) + RESERVED_IDS_PER_ASYNCACTION) { if (bpftrace->safe_mode_) { throw FatalUserException( "syscall() not allowed in safe mode. Use '--unsafe'."); } 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 arg_values = bpftrace->get_arg_values(args, arg_data); bpftrace->out_->message(MessageType::syscall, 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 arg_values = bpftrace->get_arg_values(args, arg_data); std::stringstream buf; cat_file(fmt.format_str(arg_values).c_str(), bpftrace->config_.get(ConfigKeyInt::max_cat_bytes), 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 arg_values = bpftrace->get_arg_values(args, arg_data); bpftrace->out_->message(MessageType::printf, fmt.format_str(arg_values), false); } int ringbuf_printer(void *cb_cookie, void *data, size_t size) { perf_event_printer(cb_cookie, data, size); return 0; } std::vector> BPFtrace::get_arg_values( const std::vector &args, uint8_t *arg_data) { std::vector> arg_values; for (auto arg : args) { switch (arg.type.GetTy()) { case Type::integer: if (arg.type.IsSigned()) { int64_t val = 0; switch (arg.type.GetIntBitWidth()) { case 64: val = *reinterpret_cast(arg_data + arg.offset); break; case 32: val = *reinterpret_cast(arg_data + arg.offset); break; case 16: val = *reinterpret_cast(arg_data + arg.offset); break; case 8: val = *reinterpret_cast(arg_data + arg.offset); break; case 1: val = *reinterpret_cast(arg_data + arg.offset); break; default: throw FatalUserException("get_arg_values: invalid integer size. " "8, 4, 2 and byte supported. " + std::to_string(arg.type.GetSize()) + "provided"); } arg_values.push_back(std::make_unique(val)); } else { uint64_t val = 0; switch (arg.type.GetIntBitWidth()) { case 64: val = *reinterpret_cast(arg_data + arg.offset); break; case 32: val = *reinterpret_cast(arg_data + arg.offset); break; case 16: val = *reinterpret_cast(arg_data + arg.offset); break; case 8: val = *reinterpret_cast(arg_data + arg.offset); break; case 1: val = *reinterpret_cast(arg_data + arg.offset); break; default: throw FatalUserException("get_arg_values: invalid integer size. " "8, 4, 2 and byte supported. " + std::to_string(arg.type.GetSize()) + "provided"); } // bpftrace represents enums as unsigned integers if (arg.type.IsEnumTy()) { auto enum_name = arg.type.GetName(); if (enum_defs_.contains(enum_name) && enum_defs_[enum_name].contains(val)) { arg_values.push_back( std::make_unique(val, enum_defs_[enum_name][val])); } else { arg_values.push_back( std::make_unique(val, std::to_string(val))); } } else { arg_values.push_back(std::make_unique(val)); } } break; case Type::string: { auto p = reinterpret_cast(arg_data + arg.offset); arg_values.push_back(std::make_unique( std::string(p, strnlen(p, arg.type.GetSize())), config_.get(ConfigKeyInt::max_strlen), config_.get(ConfigKeyString::str_trunc_trailer).c_str())); break; } case Type::buffer: { auto length = reinterpret_cast(arg_data + arg.offset)->length; arg_values.push_back(std::make_unique( reinterpret_cast(arg_data + arg.offset)->content, length)); break; } case Type::ksym_t: arg_values.push_back(std::make_unique(resolve_ksym( *reinterpret_cast(arg_data + arg.offset)))); break; case Type::usym_t: arg_values.push_back(std::make_unique(resolve_usym( *reinterpret_cast(arg_data + arg.offset), *reinterpret_cast(arg_data + arg.offset + 8), *reinterpret_cast(arg_data + arg.offset + 12)))); break; case Type::inet: arg_values.push_back(std::make_unique(resolve_inet( *reinterpret_cast(arg_data + arg.offset), reinterpret_cast(arg_data + arg.offset + 8)))); break; case Type::username: arg_values.push_back(std::make_unique( resolve_uid(*reinterpret_cast(arg_data + arg.offset)))); break; case Type::kstack_t: arg_values.push_back(std::make_unique( get_stack(*reinterpret_cast(arg_data + arg.offset), *reinterpret_cast(arg_data + arg.offset + 8), -1, -1, false, arg.type.stack_type, 8))); break; case Type::ustack_t: arg_values.push_back(std::make_unique( get_stack(*reinterpret_cast(arg_data + arg.offset), *reinterpret_cast(arg_data + arg.offset + 8), *reinterpret_cast(arg_data + arg.offset + 16), *reinterpret_cast(arg_data + arg.offset + 20), true, arg.type.stack_type, 8))); break; case Type::timestamp: arg_values.push_back( std::make_unique(resolve_timestamp( reinterpret_cast(arg_data + arg.offset) ->mode, reinterpret_cast(arg_data + arg.offset) ->strftime_id, reinterpret_cast(arg_data + arg.offset) ->nsecs))); break; case Type::pointer: arg_values.push_back(std::make_unique( *reinterpret_cast(arg_data + arg.offset))); break; case Type::mac_address: arg_values.push_back( std::make_unique(resolve_mac_address( reinterpret_cast(arg_data + arg.offset)))); break; case Type::cgroup_path_t: 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; case Type::strerror_t: arg_values.push_back(std::make_unique( strerror(*reinterpret_cast(arg_data + arg.offset)))); break; // fall through default: LOG(BUG) << "invalid argument type"; } } return arg_values; } void BPFtrace::add_param(const std::string ¶m) { params_.emplace_back(param); } std::string BPFtrace::get_param(size_t i, bool is_str) const { if (params_.size() < i) { return is_str ? "" : "0"; } return params_.at(i - 1); } size_t BPFtrace::num_params() const { return params_.size(); } void perf_event_lost(void *cb_cookie, uint64_t lost) { auto bpftrace = static_cast(cb_cookie); bpftrace->out_->lost_events(lost); } std::vector> BPFtrace::attach_usdt_probe( Probe &probe, const BpfProgram &program, int pid, bool file_activation) { std::vector> ret; if (feature_->has_uprobe_refcnt() || !(file_activation && probe.path.size())) { ret.emplace_back( std::make_unique(probe, program, pid, *this)); return ret; } // File activation works by scanning through /proc/*/maps and seeing // which processes have the target executable in their address space // with execute permission. If found, we will try to attach to each // process we find. // // Note that this is the slow path. If the kernel has semaphore support // (feature_->has_uprobe_refcnt()), the kernel can do this for us and // much faster too. glob_t globbuf; if (::glob("/proc/[0-9]*/maps", GLOB_NOSORT, nullptr, &globbuf)) LOG(BUG) << "failed to glob"; char *p; if (!(p = realpath(probe.path.c_str(), nullptr))) { LOG(ERROR) << "Failed to resolve " << probe.path; return ret; } std::string resolved(p); free(p); for (size_t i = 0; i < globbuf.gl_pathc; ++i) { std::string path(globbuf.gl_pathv[i]); std::ifstream file(path); if (file.fail()) { // The process could have exited between the glob and now. We have // to silently ignore that. continue; } std::string line; while (std::getline(file, line)) { if (line.find(resolved) == std::string::npos) continue; auto parts = split_string(line, ' '); if (parts.at(1).find('x') == std::string::npos) continue; // Remove `/proc/` prefix std::string pid_str(globbuf.gl_pathv[i] + 6); // No need to remove `/maps` suffix b/c stoi() will ignore trailing !ints int pid_parsed; try { pid_parsed = std::stoi(pid_str); } catch (const std::exception &ex) { throw FatalUserException("failed to parse pid=" + pid_str); } ret.emplace_back( std::make_unique(probe, program, pid_parsed, *this)); break; } } if (ret.empty()) LOG(ERROR) << "Failed to find processes running " << probe.path; return ret; } std::vector> BPFtrace::attach_probe( Probe &probe, const BpfBytecode &bytecode) { std::vector> ret; try { auto &program = bytecode.getProgramForProbe(probe); pid_t pid = child_ ? child_->pid() : this->pid(); if (probe.type == ProbeType::usdt) { auto aps = attach_usdt_probe(probe, program, pid, usdt_file_activation_); for (auto &ap : aps) ret.emplace_back(std::move(ap)); return ret; } else if (probe.type == ProbeType::uprobe || probe.type == ProbeType::uretprobe) { ret.emplace_back(std::make_unique( probe, program, pid, *this, safe_mode_)); return ret; } else if (probe.type == ProbeType::watchpoint || probe.type == ProbeType::asyncwatchpoint) { ret.emplace_back( std::make_unique(probe, program, pid, *this)); return ret; } else { ret.emplace_back(std::make_unique(probe, program, *this)); return ret; } } catch (const EnospcException &e) { // Caller will handle throw e; } catch (const std::exception &e) { LOG(ERROR) << e.what(); ret.clear(); } return ret; } bool attach_reverse(const Probe &p) { switch (p.type) { case ProbeType::special: case ProbeType::kprobe: case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::usdt: case ProbeType::software: case ProbeType::fentry: case ProbeType::iter: return true; case ProbeType::fexit: case ProbeType::kretprobe: case ProbeType::tracepoint: case ProbeType::profile: case ProbeType::interval: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::hardware: case ProbeType::rawtracepoint: return false; case ProbeType::invalid: LOG(BUG) << "Unknown probe type"; } return {}; // unreached } int BPFtrace::run_iter() { auto probe = resources.probes.begin(); char buf[1024] = {}; ssize_t len; if (probe == resources.probes.end()) { LOG(ERROR) << "Failed to create iter probe"; return 1; } // If a script contains an iter probe, it must be the only probe assert(attached_probes_.size() == 1); if (attached_probes_.empty()) { LOG(ERROR) << "Failed to attach iter probe"; return 1; } auto &ap = *attached_probes_.begin(); int link_fd = ap->linkfd_; if (link_fd < 0) { LOG(ERROR) << "Failed to link iter probe"; return 1; } if (probe->pin.empty()) { int iter_fd = bpf_iter_create(link_fd); if (iter_fd < 0) { LOG(ERROR) << "Failed to open iter probe link"; return 1; } while ((len = read(iter_fd, buf, sizeof(buf))) > 0) { fwrite(buf, len, 1, stdout); } close(iter_fd); } else { auto pin = probe->pin; if (pin.at(0) != '/') pin = "/sys/fs/bpf/" + pin; if (bpf_obj_pin(link_fd, pin.c_str())) LOG(ERROR) << "Failed to pin iter probe link"; else std::cout << "Program pinned to " << pin << std::endl; } return 0; } int BPFtrace::prerun() const { uint64_t num_probes = this->num_probes(); uint64_t max_probes = config_.get(ConfigKeyInt::max_probes); if (num_probes == 0) { if (!bt_quiet) std::cout << "No probes to attach" << std::endl; return 1; } else if (num_probes > max_probes) { LOG(ERROR) << "Can't attach to " << num_probes << " probes because it " << "exceeds the current limit of " << max_probes << " probes.\n" << "You can increase the limit through the BPFTRACE_MAX_PROBES " << "environment variable, but BE CAREFUL since a high number of probes " << "attached can cause your system to crash."; return 1; } else if (!bt_quiet) out_->attached_probes(num_probes); return 0; } int BPFtrace::run(BpfBytecode bytecode) { int err = prerun(); if (err) return err; bytecode_ = std::move(bytecode); bytecode_.set_map_ids(resources); bytecode_.update_global_vars(*this); try { bytecode_.load_progs(resources, *btf_, *feature_, config_); } catch (const HelperVerifierError &e) { if (helper_use_loc_.find(e.func_id) != helper_use_loc_.end()) { LOG(ERROR, helper_use_loc_[e.func_id], std::cerr) << e.what(); } else { LOG(ERROR) << e.what(); } return -1; } catch (const std::runtime_error &e) { LOG(ERROR) << e.what(); return -1; } err = setup_output(); if (err) return err; SCOPE_EXIT { teardown_output(); }; err = create_pcaps(); if (err) { LOG(ERROR) << "Failed to create pcap file(s)"; return err; } if (bytecode_.hasMap(MapType::Elapsed)) { struct timespec ts; clock_gettime(CLOCK_BOOTTIME, &ts); auto nsec = 1000000000ULL * ts.tv_sec + ts.tv_nsec; uint64_t key = 0; if (bpf_update_elem( bytecode_.getMap(MapType::Elapsed).fd(), &key, &nsec, 0) < 0) { perror("Failed to write start time to elapsed map"); return -1; } } auto begin_probe = resources.special_probes.find("BEGIN"); if (begin_probe != resources.special_probes.end()) { auto &begin_prog = bytecode_.getProgramForProbe((*begin_probe).second); if (::bpf_prog_test_run_opts(begin_prog.fd(), nullptr)) return -1; } auto signal_probe = resources.special_probes.find("self_signal"); if (signal_probe != resources.special_probes.end()) { auto &sig_prog = bytecode_.getProgramForProbe((*signal_probe).second); sigusr1_prog_fd_ = sig_prog.fd(); } if (child_ && has_usdt_) { try { child_->run(true); } catch (const std::exception &e) { LOG(ERROR) << "Failed to setup child: " << e.what(); return -1; } } // The kernel appears to fire some probes in the order that they were // attached and others in reverse order. In order to make sure that blocks // are executed in the same order they were declared, iterate over the probes // twice: in the first pass iterate forward and attach the probes that will // be fired in the same order they were attached, and in the second pass // iterate in reverse and attach the rest. for (auto &probe : resources.probes) { if (BPFtrace::exitsig_recv) { request_finalize(); return -1; } if (!attach_reverse(probe)) { auto aps = attach_probe(probe, bytecode_); if (aps.empty()) return -1; for (auto &ap : aps) attached_probes_.emplace_back(std::move(ap)); } } for (auto &probe : std::ranges::reverse_view(resources.probes)) { if (BPFtrace::exitsig_recv) { request_finalize(); return -1; } if (attach_reverse(probe)) { auto aps = attach_probe(probe, bytecode_); if (aps.empty()) return -1; for (auto &ap : aps) attached_probes_.emplace_back(std::move(ap)); } } if (dry_run) { request_finalize(); return 0; } // Kick the child to execute the command. if (child_) { try { if (has_usdt_) child_->resume(); else child_->run(); } catch (const std::exception &e) { LOG(ERROR) << "Failed to run child: " << e.what(); return -1; } } // Used by runtime test framework to know when to run AFTER directive if (std::getenv("__BPFTRACE_NOTIFY_PROBES_ATTACHED")) std::cout << "__BPFTRACE_NOTIFY_PROBES_ATTACHED" << std::endl; #ifdef HAVE_LIBSYSTEMD err = sd_notify(false, "READY=1\nSTATUS=Processing events..."); if (err < 0) LOG(WARNING) << "Failed to send readiness notification, ignoring: " << strerror(-err); #endif if (has_iter_) { int err = run_iter(); if (err) return err; } else { poll_output(); } #ifdef HAVE_LIBSYSTEMD err = sd_notify(false, "STOPPING=1\nSTATUS=Shutting down..."); if (err < 0) LOG(WARNING) << "Failed to send shutdown notification, ignoring: " << strerror(-err); #endif attached_probes_.clear(); // finalize_ and exitsig_recv should be false from now on otherwise // perf_event_printer() can ignore the `END` events. finalize_ = false; exitsig_recv = false; auto end_probe = resources.special_probes.find("END"); if (end_probe != resources.special_probes.end()) { auto &end_prog = bytecode_.getProgramForProbe((*end_probe).second); if (::bpf_prog_test_run_opts(end_prog.fd(), nullptr)) return -1; } poll_output(/* drain */ true); return 0; } int BPFtrace::setup_output() { if (is_ringbuf_enabled()) { setup_ringbuf(); } int err = setup_event_loss(); if (err) return err; if (is_perf_event_enabled()) { return setup_perf_events(); } return 0; } int BPFtrace::setup_perf_events() { epollfd_ = epoll_create1(EPOLL_CLOEXEC); if (epollfd_ == -1) { LOG(ERROR) << "Failed to create epollfd"; return -1; } std::vector cpus = get_online_cpus(); online_cpus_ = cpus.size(); for (int cpu : cpus) { void *reader = bpf_open_perf_buffer(&perf_event_printer, &perf_event_lost, this, -1, cpu, config_.get( ConfigKeyInt::perf_rb_pages)); if (reader == nullptr) { LOG(ERROR) << "Failed to open perf buffer"; return -1; } // Store the perf buffer pointers in a vector of unique_ptrs. // When open_perf_buffers_ is cleared or destroyed, // perf_reader_free is automatically called. open_perf_buffers_.emplace_back(reader, perf_reader_free); struct epoll_event ev = {}; ev.events = EPOLLIN; ev.data.ptr = reader; int reader_fd = perf_reader_fd(static_cast(reader)); bpf_update_elem( bytecode_.getMap(MapType::PerfEvent).fd(), &cpu, &reader_fd, 0); if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, reader_fd, &ev) == -1) { LOG(ERROR) << "Failed to add perf reader to epoll"; return -1; } } return 0; } void BPFtrace::setup_ringbuf() { ringbuf_ = static_cast(ring_buffer__new( bytecode_.getMap(MapType::Ringbuf).fd(), ringbuf_printer, this, nullptr)); } int BPFtrace::setup_event_loss() { if (bpf_update_elem(bytecode_.getMap(MapType::EventLossCounter).fd(), const_cast(&event_loss_cnt_key_), const_cast(&event_loss_cnt_val_), 0)) { LOG(ERROR) << "fail to init event loss counter"; return -1; } return 0; } void BPFtrace::teardown_output() { if (is_ringbuf_enabled()) ring_buffer__free(ringbuf_); if (is_perf_event_enabled()) // Calls perf_reader_free() on all open perf buffers. open_perf_buffers_.clear(); } void BPFtrace::poll_output(bool drain) { int ready; bool do_poll_perf_event = is_perf_event_enabled(); bool do_poll_ringbuf = is_ringbuf_enabled(); auto should_retry = [](int ready) { // epoll_wait will set errno to EINTR if an interrupt received, it is // retryable if not caused by SIGINT. ring_buffer__poll does not set errno, // we will keep retrying till SIGINT. return ready < 0 && (errno == 0 || errno == EINTR) && !BPFtrace::exitsig_recv; }; auto should_stop = [this, drain](int ready) { // Stop if either // * an exit signal is received // * epoll_wait has encountered an error (eg signal delivery) // * there's no events left and we've been instructed to drain or // finalization has been requested through exit() builtin. return BPFtrace::exitsig_recv || ready < 0 || (ready == 0 && (drain || finalize_)); }; if (do_poll_perf_event && epollfd_ < 0) { LOG(ERROR) << "Invalid epollfd " << epollfd_; return; } while (true) { if (do_poll_perf_event) { ready = poll_perf_events(); if (should_retry(ready)) { if (!do_poll_ringbuf) continue; } if (should_stop(ready)) { do_poll_perf_event = false; } } // print loss events handle_event_loss(); if (do_poll_ringbuf) { ready = ring_buffer__poll(ringbuf_, timeout_ms); if (should_retry(ready)) { continue; } if (should_stop(ready)) { do_poll_ringbuf = false; } } if (!do_poll_perf_event && !do_poll_ringbuf) { return; } // If we are tracing a specific pid and it has exited, we should exit // as well b/c otherwise we'd be tracing nothing. if ((procmon_ && !procmon_->is_alive()) || (child_ && !child_->is_alive())) { return; } if (BPFtrace::sigusr1_recv) { BPFtrace::sigusr1_recv = false; if (sigusr1_prog_fd_.has_value()) { if (::bpf_prog_test_run_opts(*sigusr1_prog_fd_, nullptr)) { LOG(ERROR) << "Failed to run signal probe"; return; } } } } return; } int BPFtrace::poll_perf_events() { auto events = std::vector(online_cpus_); int ready = epoll_wait(epollfd_, events.data(), online_cpus_, timeout_ms); if (ready <= 0) { return ready; } for (int i = 0; i < ready; i++) { perf_reader_event_read(static_cast(events[i].data.ptr)); } return ready; } void BPFtrace::handle_event_loss() { uint64_t current_value = 0; if (bpf_lookup_elem(bytecode_.getMap(MapType::EventLossCounter).fd(), const_cast(&event_loss_cnt_key_), ¤t_value)) { LOG(ERROR) << "fail to get event loss counter"; } if (current_value) { if (current_value > event_loss_count_) { out_->lost_events(current_value - event_loss_count_); event_loss_count_ = current_value; } else if (current_value < event_loss_count_) { LOG(ERROR) << "Invalid event loss count value: " << current_value << ", last seen: " << event_loss_count_; } } } int BPFtrace::print_maps() { if (dry_run) return 0; for (auto &map : bytecode_.maps()) { if (!map.second.is_printable()) continue; int err = print_map(map.second, 0, 0); if (err) return err; } return 0; } // clear a map int BPFtrace::clear_map(const BpfMap &map) { if (!map.is_clearable()) return zero_map(map); uint8_t *old_key = nullptr; auto key = std::vector(map.key_size()); // snapshot keys, then operate on them std::vector> keys; while (bpf_get_next_key(map.fd(), old_key, key.data()) == 0) { keys.push_back(key); old_key = key.data(); } for (auto &k : keys) { int err = bpf_delete_elem(map.fd(), k.data()); if (err && err != -ENOENT) { LOG(ERROR) << "failed to look up elem: " << err; return -1; } } return 0; } // zero a map int BPFtrace::zero_map(const BpfMap &map) { uint64_t nvalues = map.is_per_cpu_type() ? ncpus_ : 1; uint8_t *old_key = nullptr; auto key = std::vector(map.key_size()); // snapshot keys, then operate on them std::vector> keys; while (bpf_get_next_key(map.fd(), old_key, key.data()) == 0) { keys.push_back(key); old_key = key.data(); } int value_size = map.value_size() * nvalues; std::vector zero(value_size, 0); for (auto &k : keys) { int err = bpf_update_elem(map.fd(), k.data(), zero.data(), BPF_EXIST); if (err && err != -ENOENT) { LOG(ERROR) << "failed to look up elem: " << err; return -1; } } return 0; } int BPFtrace::print_map(const BpfMap &map, uint32_t top, uint32_t div) { const auto &map_info = resources.maps_info.at(map.name()); const auto &value_type = map_info.value_type; if (value_type.IsHistTy() || value_type.IsLhistTy()) return print_map_hist(map, top, div); uint64_t nvalues = map.is_per_cpu_type() ? ncpus_ : 1; uint8_t *old_key = nullptr; auto key = std::vector(map.key_size()); std::vector, std::vector>> values_by_key; while (bpf_get_next_key(map.fd(), old_key, key.data()) == 0) { auto value = std::vector(map.value_size() * nvalues); int err = bpf_lookup_elem(map.fd(), key.data(), value.data()); if (err == -ENOENT) { // key was removed by the eBPF program during bpf_get_next_key() and // bpf_lookup_elem(), let's skip this key continue; } else if (err) { LOG(ERROR) << "failed to look up elem: " << err; return -1; } values_by_key.push_back({ key, value }); old_key = key.data(); } if (value_type.IsCountTy() || value_type.IsSumTy() || value_type.IsIntTy()) { bool is_signed = value_type.IsSigned(); std::sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { if (is_signed) return reduce_value(a.second, nvalues) < reduce_value(b.second, nvalues); return reduce_value(a.second, nvalues) < reduce_value(b.second, nvalues); }); } else if (value_type.IsMinTy() || value_type.IsMaxTy()) { std::sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { return min_max_value(a.second, nvalues, value_type.IsMaxTy()) < min_max_value(b.second, nvalues, value_type.IsMaxTy()); }); } else if (value_type.IsAvgTy() || value_type.IsStatsTy()) { if (value_type.IsSigned()) { std::sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { return avg_value(a.second, nvalues) < avg_value(b.second, nvalues); }); } else { std::sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { return avg_value(a.second, nvalues) < avg_value(b.second, nvalues); }); } } else { sort_by_key(map_info.key_type, values_by_key); }; if (div == 0) div = 1; if (value_type.IsAvgTy() || value_type.IsStatsTy()) { out_->map_stats(*this, map, top, div, values_by_key); return 0; } out_->map(*this, map, top, div, values_by_key); return 0; } int BPFtrace::print_map_hist(const BpfMap &map, uint32_t top, uint32_t div) { // A hist-map adds an extra 8 bytes onto the end of its key for storing // the bucket number. // e.g. A map defined as: @x[1, 2] = @hist(3); // would actually be stored with the key: [1, 2, 3] uint64_t nvalues = map.is_per_cpu_type() ? ncpus_ : 1; uint8_t *old_key = nullptr; auto key = std::vector(map.key_size()); std::map, std::vector> values_by_key; const auto &map_info = resources.maps_info.at(map.name()); while (bpf_get_next_key(map.fd(), old_key, key.data()) == 0) { auto key_prefix = std::vector(map_info.key_type.GetSize()); uint64_t bucket = read_data(key.data() + map_info.key_type.GetSize()); for (size_t i = 0; i < map_info.key_type.GetSize(); i++) key_prefix.at(i) = key.at(i); auto value = std::vector(map.value_size() * nvalues); int err = bpf_lookup_elem(map.fd(), key.data(), value.data()); if (err == -ENOENT) { // key was removed by the eBPF program during bpf_get_next_key() and // bpf_lookup_elem(), let's skip this key continue; } else if (err) { LOG(ERROR) << "failed to look up elem: " << err; return -1; } if (values_by_key.find(key_prefix) == values_by_key.end()) { // New key - create a list of buckets for it if (map_info.value_type.IsHistTy()) values_by_key[key_prefix] = std::vector(65 * 32); else values_by_key[key_prefix] = std::vector(1002); } values_by_key[key_prefix].at(bucket) = reduce_value(value, nvalues); old_key = key.data(); } // Sort based on sum of counts in all buckets std::vector, uint64_t>> total_counts_by_key; for (auto &map_elem : values_by_key) { int64_t sum = 0; for (unsigned long i : map_elem.second) { sum += i; } total_counts_by_key.push_back({ map_elem.first, sum }); } std::sort(total_counts_by_key.begin(), total_counts_by_key.end(), [&](auto &a, auto &b) { return a.second < b.second; }); if (div == 0) div = 1; out_->map_hist(*this, map, top, div, values_by_key, total_counts_by_key); return 0; } std::optional BPFtrace::get_watchpoint_binary_path() const { if (child_) { // 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); assert(!args.empty()); return resolve_binary_path(args[0]).front(); } else if (pid()) return "/proc/" + std::to_string(pid()) + "/exe"; else { return std::nullopt; } } std::string BPFtrace::get_stack(int64_t stackid, uint32_t nr_stack_frames, int32_t pid, int32_t probe_id, bool ustack, StackType stack_type, int indent) { struct stack_key stack_key = { stackid, nr_stack_frames }; auto stack_trace = std::vector(stack_type.limit); int err = bpf_lookup_elem(bytecode_.getMap(stack_type.name()).fd(), &stack_key, stack_trace.data()); if (err) { // ignore EFAULT errors: eg, kstack used but no kernel stack LOG(ERROR) << "failed to look up stack id: " << stackid << " stack length: " << nr_stack_frames << " (pid " << pid << "): " << err; return ""; } std::ostringstream stack; std::string padding(indent, ' '); stack << "\n"; for (uint32_t i = 0; i < nr_stack_frames; ++i) { uint64_t addr = stack_trace.at(i); if (stack_type.mode == StackMode::raw) { stack << std::hex << addr << std::endl; continue; } std::string sym; if (!ustack) sym = resolve_ksym(addr, true); else sym = resolve_usym( addr, pid, probe_id, true, stack_type.mode == StackMode::perf); switch (stack_type.mode) { case StackMode::bpftrace: stack << padding << sym << std::endl; break; case StackMode::perf: stack << "\t" << std::hex << addr << std::dec << " " << sym << std::endl; break; case StackMode::raw: LOG(BUG) << "StackMode::raw should have been processed before " "symbolication."; break; } } return stack.str(); } std::string BPFtrace::resolve_uid(uint64_t addr) const { std::string file_name = "/etc/passwd"; std::string uid = std::to_string(addr); std::string username = ""; std::ifstream file(file_name); if (file.fail()) { LOG(ERROR) << strerror(errno) << ": " << file_name; return username; } std::string line; bool found = false; while (std::getline(file, line) && !found) { auto fields = split_string(line, ':'); if (fields.size() >= 3 && fields[2] == uid) { found = true; username = fields[0]; } } file.close(); return username; } std::string BPFtrace::resolve_timestamp(uint32_t mode, uint32_t strftime_id, uint64_t nsecs) { static const auto usec_regex = std::regex("%f"); static constexpr auto ns_in_sec = 1'000'000'000; TimestampMode ts_mode = static_cast(mode); struct timespec zero = {}; struct timespec *basetime = &zero; if (ts_mode == TimestampMode::boot) { if (!boottime_) { LOG(ERROR) << "Cannot resolve timestamp due to failed boot time calculation"; return "(?)"; } else { basetime = &boottime_.value(); } } // Calculate and localize timestamp struct tm tmp; time_t time = basetime->tv_sec + ((basetime->tv_nsec + nsecs) / ns_in_sec); if (!localtime_r(&time, &tmp)) { LOG(ERROR) << "localtime_r: " << strerror(errno); return "(?)"; } // Process strftime() format string extensions const auto &raw_fmt = resources.strftime_args[strftime_id]; uint64_t us = ((basetime->tv_nsec + nsecs) % ns_in_sec) / 1000; char usecs_buf[7]; snprintf(usecs_buf, sizeof(usecs_buf), "%06" PRIu64, us); auto fmt = std::regex_replace(raw_fmt, usec_regex, usecs_buf); uint64_t timestr_size = config_.get(ConfigKeyInt::max_strlen); std::string timestr(timestr_size, '\0'); size_t timestr_len = strftime( timestr.data(), timestr_size, fmt.c_str(), &tmp); if (timestr_len == 0) { LOG(ERROR) << "strftime returned 0"; return "(?)"; } // Fit return value to formatted length timestr.resize(timestr_len); return timestr; } std::string BPFtrace::resolve_buf(const char *buf, size_t size) { return hex_format_buffer(buf, size); } std::string BPFtrace::resolve_ksym(uint64_t addr, bool show_offset) { return ksyms_.resolve(addr, show_offset); } uint64_t BPFtrace::resolve_kname(const std::string &name) const { uint64_t addr = 0; std::string file_name = "/proc/kallsyms"; std::ifstream file(file_name); if (file.fail()) { LOG(ERROR) << strerror(errno) << ": " << file_name; return addr; } std::string line; while (std::getline(file, line) && addr == 0) { auto tokens = split_string(line, ' '); if (name == tokens[2]) { addr = read_address_from_output(line); break; } } file.close(); return addr; } uint64_t BPFtrace::resolve_cgroupid(const std::string &path) const { return bpftrace_linux::resolve_cgroupid(path); } static int sym_resolve_callback(const char *name, uint64_t addr, uint64_t size, void *payload) { struct symbol *sym = static_cast(payload); if (!strcmp(name, sym->name.c_str())) { sym->address = addr; sym->size = size; return -1; } return 0; } int BPFtrace::resolve_uname(const std::string &name, struct symbol *sym, const std::string &path) const { sym->name = name; struct bcc_symbol_option option; memset(&option, 0, sizeof(option)); option.use_symbol_type = (1 << STT_OBJECT | 1 << STT_FUNC | 1 << STT_GNU_IFUNC); return bcc_elf_foreach_sym(path.c_str(), sym_resolve_callback, &option, sym); } std::string BPFtrace::resolve_mac_address(const uint8_t *mac_addr) const { const size_t SIZE = 18; char addr[SIZE]; snprintf(addr, SIZE, "%02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); 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); } uint64_t BPFtrace::read_address_from_output(std::string output) { std::string first_word = output.substr(0, output.find(" ")); return std::stoull(first_word, nullptr, 16); } static std::string resolve_inetv4(const uint8_t *inet) { char addr_cstr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, inet, addr_cstr, INET_ADDRSTRLEN); return std::string(addr_cstr); } static std::string resolve_inetv6(const uint8_t *inet) { char addr_cstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, inet, addr_cstr, INET6_ADDRSTRLEN); return std::string(addr_cstr); } std::string BPFtrace::resolve_inet(int af, const uint8_t *inet) const { std::string addrstr; switch (af) { case AF_INET: addrstr = resolve_inetv4(inet); break; case AF_INET6: addrstr = resolve_inetv6(inet); break; } // TODO(mmarchini): handle inet_ntop errors return addrstr; } std::string BPFtrace::resolve_usym(uint64_t addr, int32_t pid, int32_t probe_id, bool show_offset, bool show_module) { if (resolve_user_symbols_) { std::string pid_exe = get_pid_exe(pid); if (pid_exe.empty() && probe_id != -1) { // sometimes program cannot be determined from PID, typically when the // process does not exist anymore; in that case, try to get program name // from probe // note: this fails if the probe contains a wildcard, since the probe id // is not generated per match auto probe_full = resolve_probe(probe_id); if (probe_full.find(',') == std::string::npos && !has_wildcard(probe_full)) { // only find program name for probes that contain one program name, // to avoid incorrect symbol resolutions size_t start = probe_full.find(':') + 1; size_t end = probe_full.find(':', start); pid_exe = probe_full.substr(start, end - start); } } return usyms_.resolve(addr, pid, pid_exe, show_offset, show_module); } else { std::ostringstream symbol; symbol << reinterpret_cast(addr); if (show_module) symbol << " ([unknown])"; return symbol.str(); } } std::string BPFtrace::resolve_probe(uint64_t probe_id) const { assert(probe_id < resources.probe_ids.size()); return resources.probe_ids[probe_id]; } void BPFtrace::sort_by_key( const SizedType &key, std::vector, std::vector>> &values_by_key) { if (key.IsTupleTy()) { // Sort the key arguments in reverse order so the results are sorted by // the first argument first, then the second, etc. auto &fields = key.GetFields(); for (size_t i = key.GetFieldCount(); i-- > 0;) { const auto &field = fields.at(i); if (field.type.IsIntTy()) { if (field.type.GetSize() == 8) { std::stable_sort( values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { auto va = read_data(a.first.data() + field.offset); auto vb = read_data(b.first.data() + field.offset); return va < vb; }); } else if (field.type.GetSize() == 4) { std::stable_sort( values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { auto va = read_data(a.first.data() + field.offset); auto vb = read_data(b.first.data() + field.offset); return va < vb; }); } else { LOG(BUG) << "invalid integer argument size. 4 or 8 expected, but " << field.type.GetSize() << " provided"; } } else if (field.type.IsStringTy()) { std::stable_sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { return strncmp(reinterpret_cast( a.first.data() + field.offset), reinterpret_cast( b.first.data() + field.offset), field.type.GetSize()) < 0; }); } } } else if (key.IsIntTy()) { if (key.GetSize() == 8) { std::stable_sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { auto va = read_data(a.first.data()); auto vb = read_data(b.first.data()); return va < vb; }); } else if (key.GetSize() == 4) { std::stable_sort(values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { auto va = read_data(a.first.data()); auto vb = read_data(b.first.data()); return va < vb; }); } else { LOG(BUG) << "invalid integer argument size. 4 or 8 expected, but " << key.GetSize() << " provided"; } } else if (key.IsStringTy()) { std::stable_sort( values_by_key.begin(), values_by_key.end(), [&](auto &a, auto &b) { return strncmp(reinterpret_cast(a.first.data()), reinterpret_cast(b.first.data()), key.GetSize()) < 0; }); } } std::string BPFtrace::get_string_literal(const ast::Expression *expr) const { if (expr->is_literal) { if (auto *string = dynamic_cast(expr)) return string->str; else if (auto *str_call = dynamic_cast(expr)) { // Positional parameters in the form str($1) can be used as literals if (str_call->func == "str") { if (auto *pos_param = dynamic_cast( str_call->vargs.at(0))) return get_param(pos_param->n, true); } } } LOG(ERROR) << "Expected string literal, got " << expr->type; return ""; } std::optional BPFtrace::get_int_literal( const ast::Expression *expr) const { if (expr->is_literal) { if (auto *integer = dynamic_cast(expr)) return integer->n; else if (auto *pos_param = dynamic_cast( expr)) { if (pos_param->ptype == PositionalParameterType::positional) { auto param_str = get_param(pos_param->n, false); auto param_int = get_int_from_str(param_str); if (!param_int.has_value()) { LOG(ERROR, pos_param->loc) << "$" << pos_param->n << " used numerically but given \"" << param_str << "\""; return std::nullopt; } if (std::holds_alternative(*param_int)) { return std::get(*param_int); } else { return static_cast(std::get(*param_int)); } } else return static_cast(num_params()); } } return std::nullopt; } const FuncsModulesMap &BPFtrace::get_traceable_funcs() const { if (traceable_funcs_.empty()) traceable_funcs_ = parse_traceable_funcs(); return traceable_funcs_; } bool BPFtrace::is_traceable_func(const std::string &func_name) const { #ifdef FUZZ (void)func_name; return true; #else auto &funcs = get_traceable_funcs(); return funcs.find(func_name) != funcs.end(); #endif } std::unordered_set BPFtrace::get_func_modules( const std::string &func_name) const { #ifdef FUZZ (void)func_name; return {}; #else auto &funcs = get_traceable_funcs(); auto mod = funcs.find(func_name); return mod != funcs.end() ? mod->second : std::unordered_set(); #endif } const struct stat &BPFtrace::get_pidns_self_stat() const { static struct stat pidns = []() -> auto { struct stat s; if (::stat("/proc/self/ns/pid", &s)) throw std::runtime_error( std::string("Failed to stat /proc/self/ns/pid: ") + std::strerror(errno)); return s; }(); return pidns; } Dwarf *BPFtrace::get_dwarf(const std::string &filename) { auto dwarf = dwarves_.find(filename); if (dwarf == dwarves_.end()) { dwarf = dwarves_.emplace(filename, Dwarf::GetFromBinary(this, filename)).first; } return dwarf->second.get(); } Dwarf *BPFtrace::get_dwarf(const ast::AttachPoint &attachpoint) { auto probe_type = probetype(attachpoint.provider); if (probe_type != ProbeType::uprobe && probe_type != ProbeType::uretprobe) return nullptr; return get_dwarf(attachpoint.target); } int BPFtrace::create_pcaps() { for (auto arg : resources.skboutput_args_) { auto file = std::get<0>(arg); if (pcap_writers_.count(file) > 0) { return 0; } auto writer = std::make_unique(); if (!writer->open(file)) return -1; pcap_writers_[file] = std::move(writer); } return 0; } void BPFtrace::close_pcaps() { for (auto &writer : pcap_writers_) { writer.second->close(); } } bool BPFtrace::write_pcaps(uint64_t id, uint64_t ns, uint8_t *pkt, unsigned int size) { if (boottime_) { ns = (boottime_->tv_sec * 1e9) + (boottime_->tv_nsec + ns); } auto file = std::get<0>(resources.skboutput_args_.at(id)); auto &writer = pcap_writers_.at(file); return writer->write(ns, pkt, size); } void BPFtrace::parse_btf(const std::set &modules) { btf_ = std::make_unique(this, modules); } bool BPFtrace::has_btf_data() const { return btf_ && btf_->has_data(); } // This prevents an ABBA deadlock when attaching to spin lock internal // functions e.g. "fentry:queued_spin_lock_slowpath". // // Specifically, if there are two hash maps (non percpu) being accessed by // two different CPUs by two bpf progs then we can get in a situation where, // because there are progs attached to spin lock internals, a lock is taken for // one map while a different lock is trying to be acquired for the other map. // This is specific to fentry/fexit (kfunc/kretfunc) as kprobes have kernel // protections against this type of deadlock. // // Note: it would be better if this was in resource analyzer but we need // probe_matcher to get the list of functions for the attach point. void BPFtrace::fentry_recursion_check(ast::Program *prog) { for (auto *probe : prog->probes) { for (auto *ap : probe->attach_points) { auto probe_type = probetype(ap->provider); if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit) { auto matches = probe_matcher_->get_matches_for_ap(*ap); for (const auto &match : matches) { if (is_recursive_func(match)) { LOG(WARNING) << "Attaching to dangerous function: " << match << ". bpftrace has added mitigations to prevent a kernel " "deadlock but they may result in some lost events."; need_recursion_check_ = true; return; } } } } } } } // namespace bpftrace bpftrace-0.23.2/src/bpftrace.h000066400000000000000000000220701477746507000161340ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "ast/ast.h" #include "attached_probe.h" #include "bpfbytecode.h" #include "bpffeature.h" #include "bpfprogram.h" #include "btf.h" #include "child.h" #include "config.h" #include "dwarf_parser.h" #include "functions.h" #include "ksyms.h" #include "output.h" #include "pcap_writer.h" #include "printf.h" #include "probe_matcher.h" #include "procmon.h" #include "required_resources.h" #include "struct.h" #include "types.h" #include "usyms.h" #include "utils.h" #include namespace bpftrace { const int timeout_ms = 100; struct stack_key { int64_t stackid; uint32_t nr_stack_frames; }; enum class DebugStage; // globals extern std::set bt_debug; extern bool bt_quiet; extern bool bt_verbose; extern bool dry_run; enum class DebugStage { Ast, Codegen, CodegenOpt, Disassemble, Libbpf, Verifier }; const std::unordered_map debug_stages = { { "ast", DebugStage::Ast }, { "codegen", DebugStage::Codegen }, { "codegen-opt", DebugStage::CodegenOpt }, #ifndef NDEBUG { "dis", DebugStage::Disassemble }, #endif { "libbpf", DebugStage::Libbpf }, { "verifier", DebugStage::Verifier }, }; class WildcardException : public std::exception { public: WildcardException(const std::string &msg) : msg_(msg) { } const char *what() const noexcept override { return msg_.c_str(); } private: std::string msg_; }; class BPFtrace { public: BPFtrace(std::unique_ptr o = std::make_unique(std::cout), BPFnofeature no_feature = BPFnofeature(), Config config = Config()) : out_(std::move(o)), feature_(std::make_unique(no_feature)), probe_matcher_(std::make_unique(this)), ncpus_(get_possible_cpus().size()), max_cpu_id_(get_max_cpu_id()), config_(config), ksyms_(config_), usyms_(config_) { } virtual ~BPFtrace(); virtual int add_probe(ast::ASTContext &ctx, const ast::AttachPoint &ap, const ast::Probe &p, int usdt_location_idx = 0); Probe generateWatchpointSetupProbe(const ast::AttachPoint &ap, const ast::Probe &probe); int num_probes() const; int prerun() const; int run(BpfBytecode bytecode); std::vector> attach_probe( Probe &probe, const BpfBytecode &bytecode); int run_iter(); int print_maps(); int clear_map(const BpfMap &map); int zero_map(const BpfMap &map); int print_map(const BpfMap &map, uint32_t top, uint32_t div); std::string get_stack(int64_t stackid, uint32_t nr_stack_frames, int32_t pid, int32_t probe_id, bool ustack, StackType stack_type, int indent = 0); std::string resolve_buf(const char *buf, size_t size); std::string resolve_ksym(uint64_t addr, bool show_offset = false); std::string resolve_usym(uint64_t addr, int32_t pid, int32_t probe_id, bool show_offset = false, bool show_module = false); std::string resolve_inet(int af, const uint8_t *inet) const; std::string resolve_uid(uint64_t addr) const; std::string resolve_timestamp(uint32_t mode, uint32_t strftime_id, uint64_t nsecs); uint64_t resolve_kname(const std::string &name) const; virtual int resolve_uname(const std::string &name, 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; std::string resolve_probe(uint64_t probe_id) const; uint64_t resolve_cgroupid(const std::string &path) const; std::vector> get_arg_values( const std::vector &args, uint8_t *arg_data); void add_param(const std::string ¶m); std::string get_param(size_t index, bool is_str) const; size_t num_params() const; void request_finalize(); std::string get_string_literal(const ast::Expression *expr) const; std::optional get_int_literal(const ast::Expression *expr) const; std::optional get_watchpoint_binary_path() const; virtual bool is_traceable_func(const std::string &func_name) const; virtual std::unordered_set get_func_modules( const std::string &func_name) const; virtual const struct stat &get_pidns_self_stat() const; bool write_pcaps(uint64_t id, uint64_t ns, uint8_t *pkt, unsigned int size); void parse_btf(const std::set &modules); bool has_btf_data() const; Dwarf *get_dwarf(const std::string &filename); Dwarf *get_dwarf(const ast::AttachPoint &attachpoint); bool has_dwarf_data() const { return !dwarves_.empty(); } void fentry_recursion_check(ast::Program *prog); std::string cmd_; bool finalize_ = false; static int exit_code; // 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; BpfBytecode bytecode_; StructManager structs; FunctionRegistry functions; std::map macros_; // Map of enum variant_name to (variant_value, enum_name) std::map> enums_; // Map of enum_name to map of variant_value to variant_name std::map> enum_defs_; std::map helper_use_loc_; const FuncsModulesMap &get_traceable_funcs() const; KConfig kconfig; std::vector> attached_probes_; std::optional sigusr1_prog_fd_; unsigned int join_argnum_ = 16; unsigned int join_argsize_ = 1024; std::unique_ptr out_; std::unique_ptr feature_; bool resolve_user_symbols_ = true; bool safe_mode_ = true; bool has_usdt_ = false; bool usdt_file_activation_ = false; int helper_check_level_ = 1; uint64_t max_ast_nodes_ = std::numeric_limits::max(); bool debug_output_ = false; std::optional boottime_; std::optional delta_taitime_; static constexpr uint32_t event_loss_cnt_key_ = 0; static constexpr uint64_t event_loss_cnt_val_ = 0; bool need_recursion_check_ = false; static void sort_by_key( const SizedType &key, std::vector, std::vector>> &values_by_key); std::unique_ptr probe_matcher_; std::unique_ptr btf_; std::unordered_set btf_set_; std::unique_ptr child_; std::unique_ptr procmon_; pid_t pid(void) const { return procmon_ ? procmon_->pid() : 0; } int ncpus_; int online_cpus_; int max_cpu_id_; Config config_; private: Ksyms ksyms_; Usyms usyms_; std::vector params_; std::vector> open_perf_buffers_; std::map> pcap_writers_; std::vector> attach_usdt_probe( Probe &probe, const BpfProgram &program, int pid, bool file_activation); int create_pcaps(); void close_pcaps(); int setup_output(); int setup_perf_events(); void setup_ringbuf(); int setup_event_loss(); // when the ringbuf feature is available, enable ringbuf for built-ins like // printf, cat. bool is_ringbuf_enabled(void) const { return feature_->has_map_ringbuf(); } // when the ringbuf feature is unavailable or built-in skboutput is used, // enable perf_event bool is_perf_event_enabled(void) const { return !feature_->has_map_ringbuf() || resources.needs_perf_event_map; } void teardown_output(); void poll_output(bool drain = false); int poll_perf_events(); void handle_event_loss(); int print_map_hist(const BpfMap &map, uint32_t top, uint32_t div); static uint64_t read_address_from_output(std::string output); struct bcc_symbol_option &get_symbol_opts(); Probe generate_probe(const ast::AttachPoint &ap, const ast::Probe &p, int usdt_location_idx = 0); bool has_iter_ = false; int epollfd_ = -1; struct ring_buffer *ringbuf_ = nullptr; uint64_t event_loss_count_ = 0; // Mapping traceable functions to modules (or "vmlinux") they appear in. // Needs to be mutable to allow lazy loading of the mapping from const lookup // functions. mutable FuncsModulesMap traceable_funcs_; std::unordered_map> dwarves_; }; } // namespace bpftrace bpftrace-0.23.2/src/btf.cpp000066400000000000000000000601321477746507000154550ustar00rootroot00000000000000#include "btf.h" #include "arch/arch.h" #include "bpftrace.h" #include "log.h" #include "probe_matcher.h" #include "tracefs.h" #include "types.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" #include #pragma GCC diagnostic pop #include #include #include "bpftrace.h" namespace bpftrace { static __u32 type_cnt(const struct btf *btf) { return btf__type_cnt(btf) - 1; } __s32 BTF::start_id(const struct btf *btf) const { return btf == vmlinux_btf ? 1 : vmlinux_btf_size + 1; } BTF::BTF(const std::set &modules) : state(NODATA) { // Try to get BTF file from BPFTRACE_BTF env char *path = std::getenv("BPFTRACE_BTF"); if (path) { btf_objects.push_back( BTFObj{ .btf = btf__parse_raw(path), .id = 0, .name = "" }); vmlinux_btf = btf_objects.back().btf; if (!vmlinux_btf) { LOG(WARNING) << "BTF: failed to parse BTF from " << path; return; } } else load_kernel_btfs(modules); if (btf_objects.empty()) { LOG(V1) << "BTF: failed to find BTF data"; return; } vmlinux_btf_size = static_cast<__s32>(type_cnt(vmlinux_btf)); state = OK; } BTF::~BTF() { for (auto &btf_obj : btf_objects) btf__free(btf_obj.btf); } void BTF::load_kernel_btfs(const std::set &modules) { vmlinux_btf = btf__load_vmlinux_btf(); if (libbpf_get_error(vmlinux_btf)) { LOG(V1) << "BTF: failed to find BTF data for vmlinux: " << strerror(errno); return; } btf_objects.push_back( BTFObj{ .btf = vmlinux_btf, .id = 0, .name = "vmlinux" }); if (bpftrace_ && !bpftrace_->feature_->has_module_btf()) return; // Note that we cannot parse BTFs from /sys/kernel/btf/ as we need BTF object // IDs, so the only way is to iterate through all loaded BTF objects __u32 id = 0; while (true) { int err = bpf_btf_get_next_id(id, &id); if (err) { if (errno != ENOENT) LOG(V1) << "BTF: failed to iterate modules BTF objects: " << strerror(errno); break; } // Get BTF object FD int fd = bpf_btf_get_fd_by_id(id); if (fd < 0) { LOG(V1) << "BTF: failed to get FD for object with id " << id; continue; } // Get BTF object info - needed to determine if this is a kernel module BTF char name[64]; struct bpf_btf_info info = {}; info.name = reinterpret_cast(name); info.name_len = sizeof(name); __u32 info_len = sizeof(info); err = bpf_obj_get_info_by_fd(fd, &info, &info_len); close(fd); // close the FD not to leave too many files open if (err) { LOG(V1) << "BTF: failed to get info for object with id " << id; continue; } auto mod_name = std::string(name); if (!info.kernel_btf) continue; if (mod_name == "vmlinux") { btf_objects.front().id = id; } else if (modules.find(mod_name) != modules.end()) { btf_objects.push_back( BTFObj{ .btf = btf__load_from_kernel_by_id_split(id, vmlinux_btf), .id = id, .name = mod_name }); } } } static void dump_printf(void *ctx, const char *fmt, va_list args) { std::string *ret = static_cast(ctx); char *str; if (vasprintf(&str, fmt, args) < 0) return; *ret += str; free(str); } static struct btf_dump *dump_new(const struct btf *btf, btf_dump_printf_fn_t dump_printf, void *ctx) { return btf_dump__new(btf, dump_printf, ctx, nullptr); } static const char *btf_str(const struct btf *btf, __u32 off) { if (!off) return "(anon)"; return btf__name_by_offset(btf, off) ?: "(invalid)"; } static std::string full_type_str(const struct btf *btf, const struct btf_type *type) { const char *str = btf_str(btf, type->name_off); if (BTF_INFO_KIND(type->info) == BTF_KIND_STRUCT) return std::string("struct ") + str; if (BTF_INFO_KIND(type->info) == BTF_KIND_UNION) return std::string("union ") + str; if (BTF_INFO_KIND(type->info) == BTF_KIND_ENUM) return std::string("enum ") + str; return str; } static std::string btf_type_str(const std::string &type) { return std::regex_replace(type, std::regex("^(struct )|(union )"), ""); } std::string BTF::dump_defs_from_btf( const struct btf *btf, std::unordered_set &types) const { std::string ret = std::string(""); struct btf_dump *dump; char err_buf[256]; int err; dump = dump_new(btf, dump_printf, &ret); err = libbpf_get_error(dump); if (err) { libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return std::string(""); } __s32 id, max = static_cast<__s32>(type_cnt(btf)); // note that we're always iterating from 1 here as we need to go through the // vmlinux BTF entries, too (even for kernel module BTFs) for (id = 1; id <= max && !types.empty(); 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)) { const struct btf_enum *p = btf_enum(t); uint16_t vlen = btf_vlen(t); for (int e = 0; e < vlen; ++e, ++p) { std::string str = btf_str(btf, p->name_off); auto it = types.find(str); if (it != types.end()) { btf_dump__dump_type(dump, id); types.erase(it); break; } } } std::string str = full_type_str(btf, t); auto it = types.find(str); if (it != types.end()) { btf_dump__dump_type(dump, id); types.erase(it); } } btf_dump__free(dump); return ret; } std::string BTF::c_def(const std::unordered_set &set) const { if (!has_data()) return std::string(""); // Definition dumping from multiple modules would require to resolve type // conflicts, so we allow dumping from a single module or from vmlinux only. std::unordered_set to_dump(set); if (btf_objects.size() == 2) { auto *mod_btf = btf_objects[0].btf == vmlinux_btf ? btf_objects[1].btf : btf_objects[0].btf; return dump_defs_from_btf(mod_btf, to_dump); } return dump_defs_from_btf(vmlinux_btf, to_dump); } std::string BTF::type_of(const std::string &name, const std::string &field) { if (!has_data()) return std::string(""); auto btf_name = btf_type_str(name); auto type_id = find_id(btf_name); if (!type_id.btf) return std::string(""); return type_of(type_id, field); } std::string BTF::type_of(const BTFId &type_id, const std::string &field) { if (!has_data()) return std::string(""); const struct btf_type *type = btf__type_by_id(type_id.btf, type_id.id); if (!type || (BTF_INFO_KIND(type->info) != BTF_KIND_STRUCT && BTF_INFO_KIND(type->info) != BTF_KIND_UNION)) return std::string(""); // We need to walk through oaa the struct/union members // and try to find the requested field name. // // More info on struct/union members: // https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-union const struct btf_member *m = btf_members(type); for (unsigned int i = 0; i < BTF_INFO_VLEN(type->info); i++) { std::string m_name = btf__name_by_offset(type_id.btf, m[i].name_off); // anonymous struct/union if (m_name == "") { std::string type_name = type_of( BTFId{ .btf = type_id.btf, .id = m[i].type }, field); if (!type_name.empty()) return type_name; } if (m_name != field) continue; const struct btf_type *f = btf__type_by_id(type_id.btf, m[i].type); if (!f) break; // Get rid of all the pointers and qualifiers on the way to the actual type. while (BTF_INFO_KIND(f->info) == BTF_KIND_PTR || BTF_INFO_KIND(f->info) == BTF_KIND_CONST || BTF_INFO_KIND(f->info) == BTF_KIND_VOLATILE || BTF_INFO_KIND(f->info) == BTF_KIND_RESTRICT) { f = btf__type_by_id(type_id.btf, f->type); if (!f) return std::string(""); } return full_type_str(type_id.btf, f); } return std::string(""); } static bool btf_type_is_modifier(const struct btf_type *t) { // Some of them is not strictly a C modifier // but they are grouped into the same bucket // for BTF concern: // A type (t) that refers to another // type through t->type AND its size cannot // be determined without following the t->type. // ptr does not fall into this bucket // because its size is always sizeof(void *). switch (BTF_INFO_KIND(t->info)) { case BTF_KIND_TYPEDEF: case BTF_KIND_VOLATILE: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: return true; default: return false; } } const struct btf_type *BTF::btf_type_skip_modifiers(const struct btf_type *t, const struct btf *btf) { while (t && btf_type_is_modifier(t)) { t = btf__type_by_id(btf, t->type); } return t; } __u32 BTF::get_type_tags(std::unordered_set &tags, const BTFId &btf_id) const { __u32 id = btf_id.id; const struct btf_type *t = btf__type_by_id(btf_id.btf, btf_id.id); while (t && btf_is_type_tag(t)) { tags.insert(btf_str(btf_id.btf, t->name_off)); id = t->type; t = btf__type_by_id(btf_id.btf, t->type); } return id; } SizedType BTF::get_stype(const BTFId &btf_id, bool resolve_structs) { const struct btf_type *t = btf__type_by_id(btf_id.btf, btf_id.id); if (!t) return CreateNone(); t = btf_type_skip_modifiers(t, btf_id.btf); auto stype = CreateNone(); if (btf_is_int(t)) { stype = CreateInteger(btf_int_bits(t), btf_int_encoding(t) & BTF_INT_SIGNED); } else if (btf_is_enum(t)) { stype = CreateInteger(t->size * 8, false); } else if (btf_is_composite(t)) { std::string cast = btf_str(btf_id.btf, t->name_off); if (cast.empty() || cast == "(anon)") return CreateNone(); std::string comp = btf_is_struct(t) ? "struct" : "union"; std::string name = comp + " " + cast; stype = CreateRecord(name, bpftrace_->structs.LookupOrAdd(name, t->size)); if (resolve_structs) resolve_fields(stype); } else if (btf_is_ptr(t)) { const BTFId pointee_btf_id = { .btf = btf_id.btf, .id = t->type }; std::unordered_set tags; auto id = get_type_tags(tags, pointee_btf_id); stype = CreatePointer( get_stype(BTFId{ .btf = btf_id.btf, .id = id }, false)); stype.SetBtfTypeTags(std::move(tags)); } else if (btf_is_array(t)) { auto *array = btf_array(t); const auto &elem_type = get_stype( BTFId{ .btf = btf_id.btf, .id = array->type }); if (elem_type.IsIntTy() && elem_type.GetSize() == 1) { stype = CreateString(array->nelems); } else { stype = CreateArray(array->nelems, elem_type); } } return stype; } std::optional BTF::resolve_args(const std::string &func, bool ret, std::string &err) { if (!has_data()) { err = "BTF data not available"; return std::nullopt; } auto func_id = find_id(func, BTF_KIND_FUNC); if (!func_id.btf) { err = "no BTF data for " + func; return std::nullopt; } const struct btf_type *t = btf__type_by_id(func_id.btf, func_id.id); t = btf__type_by_id(func_id.btf, t->type); if (!t || !btf_is_func_proto(t)) { err = func + " is not a function"; return std::nullopt; } if (bpftrace_ && !bpftrace_->is_traceable_func(func)) { if (bpftrace_->get_traceable_funcs().empty()) { err = "could not read traceable functions from " + tracefs::available_filter_functions() + " (is tracefs mounted?)"; } else { err = "function not traceable (probably it is " "inlined or marked as \"notrace\")"; } return std::nullopt; } const struct btf_param *p = btf_params(t); __u16 vlen = btf_vlen(t); if (vlen > arch::max_arg() + 1) { err = "functions with more than 6 parameters are " "not supported."; return std::nullopt; } Struct args(0, false); int j = 0; int arg_idx = 0; __u32 type_size = 0; for (; j < vlen; j++, p++) { const char *str = btf_str(func_id.btf, p->name_off); if (!str) { err = "failed to resolve arguments"; return std::nullopt; } SizedType stype = get_stype(BTFId{ .btf = func_id.btf, .id = p->type }); stype.funcarg_idx = arg_idx; stype.is_funcarg = true; args.AddField(str, stype, args.size, std::nullopt, false); // fentry args are stored in a u64 array. // Note that it's ok to represent them by a struct as we will use GEP with // funcarg_idx to access them in codegen. type_size = btf__resolve_size(func_id.btf, p->type); args.size += type_size; arg_idx += std::ceil(static_cast(type_size) / static_cast(8)); } if (ret) { SizedType stype = get_stype(BTFId{ .btf = func_id.btf, .id = t->type }); stype.funcarg_idx = arg_idx; stype.is_funcarg = true; args.AddField(RETVAL_FIELD_NAME, stype, args.size, std::nullopt, false); // fentry args (incl. retval) are stored in a u64 array args.size += btf__resolve_size(func_id.btf, t->type); } return args; } std::string BTF::get_all_funcs_from_btf(const BTFObj &btf_obj) const { std::string funcs; __s32 id, max = static_cast<__s32>(type_cnt(btf_obj.btf)); for (id = start_id(btf_obj.btf); id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf_obj.btf, id); if (!t) continue; if (!btf_is_func(t)) continue; const char *str = btf__name_by_offset(btf_obj.btf, t->name_off); std::string func_name = str; t = btf__type_by_id(btf_obj.btf, t->type); if (!t || !btf_is_func_proto(t)) { /* bad.. */ LOG(ERROR) << func_name << " function does not have FUNC_PROTO record"; break; } if (bpftrace_ && !bpftrace_->is_traceable_func(func_name)) continue; if (btf_vlen(t) > arch::max_arg() + 1) continue; funcs += btf_obj.name + ":" + std::string(func_name) + "\n"; } return funcs; } std::unique_ptr BTF::get_all_funcs() const { auto funcs = std::make_unique(); for (auto &btf_obj : btf_objects) *funcs << get_all_funcs_from_btf(btf_obj); return funcs; } std::map> BTF::get_params_from_btf( const BTFObj &btf_obj, const std::set &funcs) const { __s32 id, max = static_cast<__s32>(type_cnt(btf_obj.btf)); std::string type = std::string(""); struct btf_dump *dump; char err_buf[256]; int err; dump = dump_new(btf_obj.btf, dump_printf, &type); err = libbpf_get_error(dump); if (err) { libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return {}; } std::map> params; for (id = start_id(btf_obj.btf); id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf_obj.btf, id); if (!t) continue; if (!btf_is_func(t)) continue; const char *str = btf__name_by_offset(btf_obj.btf, t->name_off); std::string func_name = btf_obj.name + ":" + str; if (funcs.find(func_name) == funcs.end()) continue; t = btf__type_by_id(btf_obj.btf, t->type); if (!t) continue; BPFTRACE_LIBBPF_OPTS(btf_dump_emit_type_decl_opts, decl_opts, .field_name = ""); const struct btf_param *p; int j; for (j = 0, p = btf_params(t); j < btf_vlen(t); j++, p++) { // set by dump_printf callback type = std::string(""); const char *arg_name = btf__name_by_offset(btf_obj.btf, p->name_off); err = btf_dump__emit_type_decl(dump, p->type, &decl_opts); if (err) { LOG(ERROR) << "failed to dump argument: " << arg_name; break; } params[func_name].push_back(type + " " + arg_name); } if (!t->type) continue; // set by dump_printf callback type = std::string(""); err = btf_dump__emit_type_decl(dump, t->type, &decl_opts); if (err) { LOG(ERROR) << "failed to dump return type for: " << func_name; break; } params[func_name].push_back(type + " retval"); } if (id != (max + 1)) LOG(ERROR) << "BTF data inconsistency " << id << "," << max; btf_dump__free(dump); return params; } std::map> BTF::get_params( const std::set &funcs) const { std::map> params; auto all_resolved = [¶ms](const std::string &f) { return params.find(f) != params.end(); }; for (auto &btf_obj : btf_objects) { if (std::all_of(funcs.begin(), funcs.end(), all_resolved)) break; auto mod_params = get_params_from_btf(btf_obj, funcs); params.insert(mod_params.begin(), mod_params.end()); } return params; } std::set BTF::get_all_structs_from_btf(const struct btf *btf) const { std::set struct_set; __s32 id, max = static_cast<__s32>(type_cnt(btf)); std::string types = std::string(""); struct btf_dump *dump; char err_buf[256]; int err; dump = dump_new(btf, dump_printf, &types); err = libbpf_get_error(dump); if (err) { libbpf_strerror(err, err_buf, sizeof(err_buf)); LOG(ERROR) << "BTF: failed to initialize dump (" << err_buf << ")"; return {}; } for (id = start_id(btf); id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf, id); if (!t || !(btf_is_struct(t) || btf_is_union(t) || btf_is_enum(t))) continue; const std::string name = full_type_str(btf, t); if (name.find("(anon)") != std::string::npos) continue; if (bt_verbose) btf_dump__dump_type(dump, id); else struct_set.insert(name); } if (id != (max + 1)) LOG(ERROR) << " BTF data inconsistency " << id << "," << max; btf_dump__free(dump); if (bt_verbose) { // BTF dump contains definitions of all types in a single string, here we // split it std::istringstream type_stream(types); std::string line, type; bool in_def = false; while (std::getline(type_stream, line)) { if (in_def) { type += line + "\n"; if (line == "};") { // end of type definition struct_set.insert(type); type.clear(); in_def = false; } } else if (!line.empty() && line.back() == '{') { // start of type definition type += line + "\n"; in_def = true; } } } return struct_set; } std::set BTF::get_all_structs() const { std::set structs; for (auto &btf_obj : btf_objects) { auto mod_structs = get_all_structs_from_btf(btf_obj.btf); structs.insert(mod_structs.begin(), mod_structs.end()); } return structs; } std::unordered_set BTF::get_all_iters_from_btf( const struct btf *btf) const { std::unordered_set iter_set; const std::string prefix = "bpf_iter__"; // kernel commit 6fcd486b3a0a("bpf: Refactor RCU enforcement in the // verifier.") add 'struct bpf_iter__task__safe_trusted' const std::string suffix___safe_trusted = "__safe_trusted"; __s32 id, max = static_cast<__s32>(type_cnt(btf)); for (id = start_id(btf); id <= max; id++) { const struct btf_type *t = btf__type_by_id(btf, id); if (!t || !(btf_is_struct(t))) continue; const std::string name = btf_str(btf, t->name_off); // skip __safe_trusted suffix struct if (suffix___safe_trusted.length() < name.length() && name.rfind(suffix___safe_trusted) == (name.length() - suffix___safe_trusted.length())) continue; if (name.size() > prefix.size() && name.compare(0, prefix.size(), prefix) == 0) { iter_set.insert(name.substr(prefix.size())); } } return iter_set; } std::unordered_set BTF::get_all_iters() const { std::unordered_set iters; for (auto &btf_obj : btf_objects) { auto mod_iters = get_all_iters_from_btf(btf_obj.btf); iters.insert(mod_iters.begin(), mod_iters.end()); } return iters; } int BTF::get_btf_id(std::string_view func, std::string_view mod) const { for (auto &btf_obj : btf_objects) { if (!mod.empty() && mod != btf_obj.name) continue; auto id = find_id_in_btf(btf_obj.btf, func, BTF_KIND_FUNC); if (id >= 0) return id; } return -1; } BTF::BTFId BTF::find_id(const std::string &name, std::optional<__u32> kind) const { for (auto &btf_obj : btf_objects) { __s32 id = kind ? btf__find_by_name_kind(btf_obj.btf, name.c_str(), *kind) : btf__find_by_name(btf_obj.btf, name.c_str()); if (id >= 0) return { btf_obj.btf, static_cast<__u32>(id) }; } return { nullptr, 0 }; } __s32 BTF::find_id_in_btf(struct btf *btf, std::string_view name, std::optional<__u32> kind) const { for (__s32 id = start_id(btf), max = static_cast<__s32>(type_cnt(btf)); id <= max; ++id) { const struct btf_type *t = btf__type_by_id(btf, id); if (!t) continue; if (kind && btf_kind(t) != *kind) continue; std::string type_name = btf__name_by_offset(btf, t->name_off); if (type_name == name) return id; } return -1; } void BTF::resolve_fields(SizedType &type) { if (!type.IsRecordTy()) return; auto record = bpftrace_->structs.Lookup(type.GetName()).lock(); if (record->HasFields()) return; auto type_name = btf_type_str(type.GetName()); auto type_id = find_id(type_name, BTF_KIND_STRUCT); if (!type_id.btf) type_id = find_id(type_name, BTF_KIND_UNION); if (!type_id.btf) return; resolve_fields(type_id, record.get(), 0); } static std::optional resolve_bitfield( const struct btf_type *record_type, __u32 member_idx) { __u32 bitfield_width = btf_member_bitfield_size(record_type, member_idx); if (bitfield_width <= 0) return std::nullopt; return Bitfield(btf_member_bit_offset(record_type, member_idx) % 8, bitfield_width); } void BTF::resolve_fields(const BTFId &type_id, Struct *record, __u32 start_offset) { auto btf_type = btf__type_by_id(type_id.btf, type_id.id); if (!btf_type) return; auto members = btf_members(btf_type); for (__u32 i = 0; i < BTF_INFO_VLEN(btf_type->info); i++) { BTFId field_id{ .btf = type_id.btf, .id = members[i].type }; auto field_type = btf__type_by_id(field_id.btf, field_id.id); if (!field_type) { LOG(ERROR) << "Inconsistent BTF data (no type found for id " << members[i].type << ")"; record->ClearFields(); break; } std::string field_name = btf__name_by_offset(type_id.btf, members[i].name_off); __u32 field_offset = start_offset + btf_member_bit_offset(btf_type, i) / 8; if (btf_is_composite(field_type) && (field_name.empty() || field_name == "(anon)")) { resolve_fields(field_id, record, field_offset); continue; } record->AddField(field_name, get_stype(field_id), field_offset, resolve_bitfield(btf_type, i), false); } } SizedType BTF::get_stype(const std::string &type_name) { auto btf_name = btf_type_str(type_name); auto type_id = find_id(btf_name); if (type_id.btf) return get_stype(type_id); return CreateNone(); } SizedType BTF::get_var_type(const std::string &var_name) { auto var_id = find_id(var_name, BTF_KIND_VAR); if (!var_id.btf) return CreateNone(); const struct btf_type *t = btf__type_by_id(var_id.btf, var_id.id); if (!t) return CreateNone(); return get_stype(BTFId{ .btf = var_id.btf, .id = t->type }); } } // namespace bpftrace bpftrace-0.23.2/src/btf.h000066400000000000000000000103271477746507000151230ustar00rootroot00000000000000#pragma once #include "types.h" #include #include #include #include #include #include #include #include #include // Taken from libbpf #define BTF_INFO_ENC(kind, kind_flag, vlen) \ ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN)) #define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type) #define BTF_INT_ENC(encoding, bits_offset, nr_bits) \ ((encoding) << 24 | (bits_offset) << 16 | (nr_bits)) #define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \ BTF_INT_ENC(encoding, bits_offset, bits) #define BTF_PARAM_ENC(name, type) (name), (type) struct btf; struct btf_type; namespace bpftrace { class BPFtrace; class BTF { enum state { NODATA, OK, }; // BTF object for vmlinux or a kernel module. // We're currently storing its name and BTF id. struct BTFObj { struct btf* btf; __u32 id; std::string name; }; // It is often necessary to store a BTF id along with the BTF data containing // its definition. struct BTFId { struct btf* btf; __u32 id; }; public: BTF(const std::set& modules); BTF(BPFtrace* bpftrace, const std::set& modules) : BTF(modules) { bpftrace_ = bpftrace; }; ~BTF(); bool has_data(void) const; size_t objects_cnt() const { return btf_objects.size(); } std::string c_def(const std::unordered_set& set) const; std::string type_of(const std::string& name, const std::string& field); std::string type_of(const BTFId& type_id, const std::string& field); SizedType get_stype(const std::string& type_name); SizedType get_var_type(const std::string& var_name); std::set get_all_structs() const; std::unique_ptr get_all_funcs() const; std::unordered_set get_all_iters() const; std::map> get_params( const std::set& funcs) const; std::optional resolve_args(const std::string& func, bool ret, std::string& err); void resolve_fields(SizedType& type); int get_btf_id(std::string_view func, std::string_view mod) const; private: void load_kernel_btfs(const std::set& modules); SizedType get_stype(const BTFId& btf_id, bool resolve_structs = true); void resolve_fields(const BTFId& type_id, Struct* record, __u32 start_offset); const struct btf_type* btf_type_skip_modifiers(const struct btf_type* t, const struct btf* btf); BTF::BTFId find_id(const std::string& name, std::optional<__u32> kind = std::nullopt) const; __s32 find_id_in_btf(struct btf* btf, std::string_view name, std::optional<__u32> = std::nullopt) const; std::string dump_defs_from_btf(const struct btf* btf, std::unordered_set& types) const; std::string get_all_funcs_from_btf(const BTFObj& btf_obj) const; std::map> get_params_from_btf( const BTFObj& btf_obj, const std::set& funcs) const; std::set get_all_structs_from_btf(const struct btf* btf) const; std::unordered_set get_all_iters_from_btf( const struct btf* btf) const; // Similar to btf_type_skip_modifiers this returns the id of the first // type that is not a BTF_KIND_TYPE_TAG while also populating the tags set // with the tag/attribute names from the BTF_KIND_TYPE_TAG types it finds. __u32 get_type_tags(std::unordered_set& tags, const BTFId& btf_id) const; __s32 start_id(const struct btf* btf) const; struct btf* vmlinux_btf = nullptr; __s32 vmlinux_btf_size; // BTF objects for vmlinux and modules std::vector btf_objects; enum state state = NODATA; BPFtrace* bpftrace_ = nullptr; }; inline bool BTF::has_data(void) const { return state == OK; } } // namespace bpftrace bpftrace-0.23.2/src/build_info.cpp000066400000000000000000000014701477746507000170140ustar00rootroot00000000000000#include #include #include "build_info.h" #include "version.h" namespace bpftrace { std::string BuildInfo::report() { std::stringstream buf; buf << "Build" << std::endl << " version: " << BPFTRACE_VERSION << std::endl << " LLVM: " << LLVM_VERSION_MAJOR << "." << LLVM_VERSION_MINOR << "." << LLVM_VERSION_PATCH << std::endl << " bfd: " #ifdef HAVE_BFD_DISASM << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " liblldb (DWARF support): " #ifdef HAVE_LIBLLDB << "yes" << std::endl; #else << "no" << std::endl; #endif buf << " libsystemd (systemd notify support): " #ifdef HAVE_LIBSYSTEMD << "yes" << std::endl; #else << "no" << std::endl; #endif return buf.str(); } } // namespace bpftrace bpftrace-0.23.2/src/build_info.h000066400000000000000000000002141477746507000164540ustar00rootroot00000000000000#pragma once #include namespace bpftrace { class BuildInfo { public: static std::string report(); }; } // namespace bpftrace bpftrace-0.23.2/src/child.cpp000066400000000000000000000162661477746507000157760ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "child.h" #include "log.h" #include "utils.h" extern char** environ; namespace bpftrace { constexpr unsigned int maxargs = 256; constexpr uint64_t CHILD_GO = 'g'; constexpr uint64_t CHILD_PTRACE = 'p'; constexpr unsigned int STACK_SIZE = (64 * 1024UL); std::system_error SYS_ERROR(std::string msg) { return std::system_error(errno, std::generic_category(), msg); } static void report_status(int wstatus) { std::stringstream msg; if (WIFSTOPPED(wstatus)) msg << "Child stopped unexpectedly, signal: " << WSTOPSIG(wstatus); else if (WIFEXITED(wstatus)) msg << "Child exited unexpectedly"; else if (WIFSIGNALED(wstatus)) { if (WCOREDUMP(wstatus)) msg << "Child core dumped"; else msg << "Child aborted by signal: " << WTERMSIG(wstatus); } throw std::runtime_error(msg.str()); } static int childfn(void* arg) { struct child_args* args = static_cast(arg); // Receive SIGTERM if parent dies if (prctl(PR_SET_PDEATHSIG, SIGTERM)) { perror("child: prctl(PR_SET_PDEATHSIG)"); return 10; } // Convert vector of strings into raw array of C-strings for execve(2) char* argv[maxargs]; int idx = 0; for (const auto& a : args->cmd) { argv[idx++] = const_cast(a.c_str()); } argv[idx] = nullptr; // must be null terminated uint64_t bf; int ret = read(args->event_fd, &bf, sizeof(bf)); if (ret < 0) { perror("child: failed to read 'go' event fd"); return 11; } close(args->event_fd); if (bf == CHILD_PTRACE) { if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) perror("child: ptrace(traceme) failed"); if (kill(getpid(), SIGSTOP)) perror("child: failed to stop"); } execve(argv[0], argv, environ); auto err = "child: failed to execve: " + std::string(argv[0]); perror(err.c_str()); return 12; } static void validate_cmd(std::vector& cmd) { auto paths = resolve_binary_path(cmd[0]); switch (paths.size()) { case 0: throw std::runtime_error("path '" + cmd[0] + "' does not exist or is not executable"); case 1: cmd[0] = paths.front().c_str(); break; default: // /bin maybe is a symbolic link to /usr/bin (/bin -> /usr/bin), there // may be worse cases like: // $ realpath /usr/bin/ping /bin/ping /usr/sbin/ping /sbin/ping // /usr/bin/ping // /usr/bin/ping // /usr/bin/ping // /usr/bin/ping std::unordered_set uniq_abs_path; for (const auto& path : paths) { auto absolute = abs_path(path); if (!absolute.has_value()) continue; uniq_abs_path.insert(*absolute); } if (uniq_abs_path.size() == 1) { cmd[0] = paths.front().c_str(); break; } else { throw std::runtime_error( "path '" + cmd[0] + "' must refer to a unique binary but matched " + std::to_string(paths.size()) + " binaries"); } } if (cmd.size() >= (maxargs - 1)) { throw std::runtime_error("Too many arguments for command (" + std::to_string(cmd.size()) + " > " + std::to_string(maxargs - 1) + ")"); } } ChildProc::ChildProc(std::string cmd) { auto child_args = std::make_unique(); auto child_stack = std::make_unique(STACK_SIZE); child_args->cmd = split_string(cmd, ' '); validate_cmd(child_args->cmd); int event_fd = eventfd(0, EFD_CLOEXEC); if (event_fd < 0) { SYS_ERROR("Failed to create event fd"); } child_args->event_fd = event_fd; child_event_fd_ = event_fd; pid_t cpid = clone( childfn, child_stack.get() + STACK_SIZE, SIGCHLD, child_args.get()); if (cpid <= 0) { close(event_fd); throw SYS_ERROR("Failed to clone child"); } child_pid_ = cpid; state_ = State::FORKED; } ChildProc::~ChildProc() { if (child_event_fd_ >= 0) { close(child_event_fd_); } if (is_alive()) terminate(true); } bool ChildProc::is_alive() { if (!died()) check_child(); return !died(); } void ChildProc::terminate(bool force) { // Make sure child didn't terminate in mean time check_child(); if (died()) return; if (child_pid_ <= 1) LOG(BUG) << "child_pid <= 1"; int sig = force ? SIGKILL : SIGTERM; kill(child_pid_, sig); if (state_ == State::PTRACE_PAUSE) ptrace(PTRACE_DETACH, child_pid_, nullptr, 0); check_child(force); } void ChildProc::resume() { assert(state_ == State::PTRACE_PAUSE); ptrace(PTRACE_DETACH, child_pid_, nullptr, 0); } void ChildProc::run(bool pause) { if (!is_alive()) { throw std::runtime_error("Child died unexpectedly"); } assert(state_ == State::FORKED); auto* data = pause ? &CHILD_PTRACE : &CHILD_GO; if (write(child_event_fd_, data, sizeof(*data)) < 0) { close(child_event_fd_); terminate(true); throw SYS_ERROR("Failed to write 'go' event fd"); } close(child_event_fd_); if (!pause) { state_ = State::RUNNING; return; } state_ = State::PTRACE_PAUSE; // After receiving the ptrace message the child will setup // ptrace and SIGSTOP itself. // we can then setup ptrace to stop the child right after execve // and let the child run until that point int wstatus; if (waitpid(child_pid_, &wstatus, 0) < 0) { if (errno == ECHILD) throw std::runtime_error("Child died unexpectedly"); } if (!WIFSTOPPED(wstatus) || WSTOPSIG(wstatus) != SIGSTOP) report_status(wstatus); try { if (ptrace(PTRACE_SETOPTIONS, child_pid_, nullptr, PTRACE_O_TRACEEXEC) < 0) throw SYS_ERROR("Failed to PTRACE_SETOPTIONS child"); if (ptrace(PTRACE_CONT, child_pid_, nullptr, 0) < 0) throw SYS_ERROR("Failed to PTRACE_CONT child"); if (waitpid(child_pid_, &wstatus, 0) < 0) throw SYS_ERROR("Error while waiting for child"); if (WIFSTOPPED(wstatus) && wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) return; report_status(wstatus); } catch (const std::runtime_error& e) { ptrace(PTRACE_DETACH, child_pid_, nullptr, 0); terminate(true); throw SYS_ERROR("Failed to write 'go' event fd"); } } // private void ChildProc::check_wstatus(int wstatus) { if (WIFEXITED(wstatus)) exit_code_ = WEXITSTATUS(wstatus); else if (WIFSIGNALED(wstatus)) term_signal_ = WTERMSIG(wstatus); // Ignore STOP and CONT else return; state_ = State::DIED; } void ChildProc::check_child(bool block) { int status = 0; int flags = WNOHANG; if (block) flags &= ~WNOHANG; pid_t ret; while ((ret = waitpid(child_pid_, &status, flags)) < 0 && errno == EINTR) ; if (ret < 0) { if (errno == EINVAL) LOG(BUG) << "waitpid() EINVAL"; else { LOG(WARNING) << "waitpid(" << child_pid_ << ") returned unexpected error: " << errno << ". Marking the child as dead"; state_ = State::DIED; return; } } if (ret == 0) return; check_wstatus(status); } } // namespace bpftrace bpftrace-0.23.2/src/child.h000066400000000000000000000055101477746507000154310ustar00rootroot00000000000000#pragma once #include #include #include #include namespace bpftrace { struct child_args { std::vector cmd; int event_fd; }; class ChildProcBase { public: // Parse command and fork a child process. // // \param cmd Command to run ChildProcBase() = default; virtual ~ChildProcBase() = default; // let child run (execve). // // \param pause If set the child will be paused(stopped) just // after `execve`. To resume the child `resume` will have to // be called. virtual void run(bool pause = false) = 0; // Ask child to terminate // // \param force Forcefully kill the child (SIGKILL) virtual void terminate(bool force = false) = 0; // Whether the child process is still alive or not virtual bool is_alive() = 0; // return the child pid pid_t pid() { return child_pid_; }; // Get child exit code, if any. This should only be called when the child has // finished (i.e. when is_alive() returns false) // // \return The exit code of the child or -1 if the child hasn't been // terminated (by a signal) // int exit_code() { return exit_code_; }; // Get termination signal, if any. This should only be called when the child // has finished (i.e. when is_alive() returns false) // // \return A signal ID or -1 if the child hasn't been terminated (by a signal) int term_signal() { return term_signal_; }; // Resume a paused child. Only valid when run() has been called with // pause=true virtual void resume(void) = 0; protected: pid_t child_pid_ = -1; int exit_code_ = -1; int term_signal_ = -1; }; class ChildProc : public ChildProcBase { public: // Parse command and fork a child process. The child is run with the same // permissions and environment variables as bpftrace. // // \param the command to run, with up to 255 optional arguments. If the // executables path isn't fully specified it the current PATH will be // searched. If more than one binary with the same name is found in the PATH // an exception is raised. // ChildProc(std::string cmd); ~ChildProc(); // Disallow copying as the internal state will get out of sync which will // cause issues. ChildProc(const ChildProc&) = delete; ChildProc& operator=(const ChildProc&) = delete; ChildProc(ChildProc&&) = delete; ChildProc& operator=(ChildProc&&) = delete; void run(bool pause = false) override; void terminate(bool force = false) override; bool is_alive() override; void resume(void) override; private: enum class State { INIT, FORKED, RUNNING, DIED, PTRACE_PAUSE, }; State state_ = State::INIT; void check_child(bool block = false); void check_wstatus(int wstatus); bool died() { return state_ == State::DIED; }; int child_event_fd_ = -1; }; } // namespace bpftrace bpftrace-0.23.2/src/clang_parser.cpp000066400000000000000000000647021477746507000173510ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "llvm/Config/llvm-config.h" #include "ast/ast.h" #include "ast/passes/field_analyser.h" #include "btf.h" #include "clang_parser.h" #include "log.h" #include "resources/headers.h" #include "types.h" #include "utils.h" namespace bpftrace { namespace { const std::vector &getDefaultHeaders() { static std::vector unsaved_files = { { .Filename = "/bpftrace/include/__stddef_max_align_t.h", .Contents = __stddef_max_align_t_h.data(), .Length = __stddef_max_align_t_h.length(), }, { .Filename = "/bpftrace/include/float.h", .Contents = float_h.data(), .Length = float_h.length(), }, { .Filename = "/bpftrace/include/limits.h", .Contents = limits_h.data(), .Length = limits_h.length(), }, { .Filename = "/bpftrace/include/stdarg.h", .Contents = stdarg_h.data(), .Length = stdarg_h.length(), }, { .Filename = "/bpftrace/include/stdbool.h", .Contents = stdbool_h.data(), .Length = stdbool_h.length(), }, { .Filename = "/bpftrace/include/stddef.h", .Contents = stddef_h.data(), .Length = stddef_h.length(), }, { .Filename = "/bpftrace/include/stdint.h", .Contents = stdint_h.data(), .Length = stdint_h.length(), }, }; return unsaved_files; } std::vector getTranslationUnitFiles( const CXUnsavedFile &main_file) { std::vector files; files.reserve(1 + files.size()); files.emplace_back(main_file); const auto &dfl = getDefaultHeaders(); files.insert(files.end(), dfl.cbegin(), dfl.cend()); return files; } } // namespace static std::string get_clang_string(CXString string) { std::string str = clang_getCString(string); clang_disposeString(string); return str; } // get_named_parent // // Find the parent struct of the field pointed to by the cursor. // Anonymous structs are skipped. static CXCursor get_named_parent(CXCursor c) { CXCursor parent = clang_getCursorSemanticParent(c); while (!clang_Cursor_isNull(parent) && clang_Cursor_isAnonymousRecordDecl(parent)) { parent = clang_getCursorSemanticParent(parent); } return parent; } static std::optional getBitfield(CXCursor c) { if (!clang_Cursor_isBitField(c)) { return std::nullopt; } return Bitfield(clang_Cursor_getOffsetOfField(c) % 8, clang_getFieldDeclBitWidth(c)); } // NOTE(mmarchini): as suggested in // http://clang-developers.42468.n3.nabble.com/Extracting-macro-information-using-libclang-the-C-Interface-to-Clang-td4042648.html#message4042666 static bool translateMacro(CXCursor cursor, std::string &name, std::string &value) { CXToken *tokens = nullptr; unsigned numTokens = 0; CXTranslationUnit transUnit = clang_Cursor_getTranslationUnit(cursor); CXSourceRange srcRange = clang_getCursorExtent(cursor); clang_tokenize(transUnit, srcRange, &tokens, &numTokens); for (unsigned n = 0; n < numTokens; n++) { auto tokenText = clang_getTokenSpelling(transUnit, tokens[n]); if (n == 0) { value.clear(); name = clang_getCString(tokenText); } else { CXTokenKind tokenKind = clang_getTokenKind(tokens[n]); if (tokenKind != CXToken_Comment) { const char *text = clang_getCString(tokenText); if (text) value += text; } } clang_disposeString(tokenText); } clang_disposeTokens(transUnit, tokens, numTokens); return value.length() != 0; } static std::string remove_qualifiers(std::string &&typestr) { // libclang prints "const" keyword first // https://github.com/llvm-mirror/clang/blob/65acf43270ea2894dffa0d0b292b92402f80c8cb/lib/AST/TypePrinter.cpp#L137-L157 static std::regex re("^(const volatile\\s+)|^(const\\s+)|" "^(volatile\\s+)|\\*(\\s*restrict)$"); return std::regex_replace(typestr, re, ""); } static std::string get_unqualified_type_name(CXType clang_type) { return remove_qualifiers(get_clang_string(clang_getTypeSpelling(clang_type))); } static SizedType get_sized_type(CXType clang_type, StructManager &structs) { auto size = 8 * clang_Type_getSizeOf(clang_type); auto typestr = get_unqualified_type_name(clang_type); switch (clang_type.kind) { case CXType_Bool: case CXType_Char_U: case CXType_UChar: case CXType_UShort: case CXType_UInt: case CXType_ULong: case CXType_ULongLong: return CreateUInt(size); case CXType_Record: { // Struct map entry may not exist for forward declared types so we create // it now and fill it later auto s = structs.LookupOrAdd(typestr, size / 8); return CreateRecord(typestr, s); } case CXType_Char_S: case CXType_SChar: case CXType_Short: case CXType_Long: case CXType_LongLong: case CXType_Int: return CreateInt(size); case CXType_Enum: { // The pretty printed type name contains `enum` prefix. That's not // helpful for us, so remove it. We have our own metadata. static std::regex re("enum "); auto enum_name = std::regex_replace(typestr, re, ""); return CreateEnum(size, enum_name); } case CXType_Pointer: { auto pointee_type = clang_getPointeeType(clang_type); return CreatePointer(get_sized_type(pointee_type, structs)); } case CXType_ConstantArray: { auto elem_type = clang_getArrayElementType(clang_type); auto size = clang_getNumElements(clang_type); if (elem_type.kind == CXType_Char_S || elem_type.kind == CXType_Char_U) { return CreateString(size); } auto elem_stype = get_sized_type(elem_type, structs); return CreateArray(size, elem_stype); } default: return CreateNone(); } } ClangParser::ClangParserHandler::ClangParserHandler() { index = clang_createIndex(1, 1); } ClangParser::ClangParserHandler::~ClangParserHandler() { clang_disposeTranslationUnit(translation_unit); clang_disposeIndex(index); } CXTranslationUnit ClangParser::ClangParserHandler::get_translation_unit() { return translation_unit; } CXErrorCode ClangParser::ClangParserHandler::parse_translation_unit( const char *source_filename, const char *const *command_line_args, int num_command_line_args, struct CXUnsavedFile *unsaved_files, unsigned num_unsaved_files, unsigned options) { // Clean up previous translation unit to prevent resource leak clang_disposeTranslationUnit(translation_unit); return clang_parseTranslationUnit2(index, source_filename, command_line_args, num_command_line_args, unsaved_files, num_unsaved_files, options, &translation_unit); } bool ClangParser::ClangParserHandler::check_diagnostics(bool bail_on_error) { for (unsigned int i = 0; i < clang_getNumDiagnostics(get_translation_unit()); i++) { CXDiagnostic diag = clang_getDiagnostic(get_translation_unit(), i); CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(diag); std::string msg = clang_getCString(clang_getDiagnosticSpelling(diag)); error_msgs.push_back(msg); if ((bail_on_error && severity == CXDiagnostic_Error) || severity == CXDiagnostic_Fatal) { // Do not fail on "too many errors" if (!bail_on_error && msg == "too many errors emitted, stopping now") return true; return false; } } return true; } CXCursor ClangParser::ClangParserHandler::get_translation_unit_cursor() { return clang_getTranslationUnitCursor(translation_unit); } bool ClangParser::ClangParserHandler::parse_file( const std::string &filename, const std::vector &args, std::vector &unsaved_files, bool bail_on_errors) { StderrSilencer silencer; if (!bail_on_errors) silencer.silence(); CXErrorCode error = parse_translation_unit( filename.c_str(), args.data(), args.size(), unsaved_files.data(), unsaved_files.size(), CXTranslationUnit_DetailedPreprocessingRecord); error_msgs.clear(); if (error) { LOG(V1) << "Clang error while parsing C definitions: " << error; return false; } return check_diagnostics(bail_on_errors); } const std::vector &ClangParser::ClangParserHandler:: get_error_messages() { return error_msgs; } bool ClangParser::ClangParserHandler::has_redefinition_error() { for (auto &msg : error_msgs) { if (msg.find("redefinition") != std::string::npos) return true; } return false; } bool ClangParser::ClangParserHandler::has_unknown_type_error() { for (auto &msg : error_msgs) { if (ClangParser::get_unknown_type(msg).has_value()) return true; } return false; } namespace { // Get annotation associated with field declaration `c` std::optional get_field_decl_annotation(CXCursor c) { assert(clang_getCursorKind(c) == CXCursor_FieldDecl); std::optional annotation; clang_visitChildren( c, [](CXCursor c, CXCursor __attribute__((unused)) parent, CXClientData data) { // The header generation code can annotate some struct // fields with additional information for us to parse // here. The annotation looks like: // // struct Foo { // __attribute__((annotate("tp_data_loc"))) int // name; // }; // // Currently only the TracepointFormatParser does this. if (clang_getCursorKind(c) == CXCursor_AnnotateAttr) { auto &a = *static_cast *>(data); a = get_clang_string(clang_getCursorSpelling(c)); } return CXChildVisit_Recurse; }, &annotation); return annotation; } } // namespace bool ClangParser::visit_children(CXCursor &cursor, BPFtrace &bpftrace) { int err = clang_visitChildren( cursor, [](CXCursor c, CXCursor parent, CXClientData client_data) { if (clang_getCursorKind(c) == CXCursor_MacroDefinition) { std::string macro_name; std::string macro_value; if (translateMacro(c, macro_name, macro_value)) { auto ¯os = static_cast(client_data)->macros_; macros[macro_name] = macro_value; } return CXChildVisit_Recurse; } // Each anon enum must have a unique ID otherwise two variants // with different names but same value will clobber each other // in enum_defs_. static uint32_t anon_enum_count = 0; if (clang_getCursorKind(c) == CXCursor_EnumDecl) anon_enum_count++; if (clang_getCursorKind(parent) == CXCursor_EnumDecl) { // Store variant name to variant value auto &enums = static_cast(client_data)->enums_; auto enum_name = get_clang_string(clang_getCursorSpelling(parent)); // Anonymous enums have empty string names in libclang <= 15 if (enum_name.empty()) { std::ostringstream name; name << "enum "; enum_name = std::move(name.str()); } auto variant_name = get_clang_string(clang_getCursorSpelling(c)); auto variant_value = clang_getEnumConstantDeclValue(c); enums[variant_name] = std::make_pair(variant_value, enum_name); // Store enum name to variant value to variant name auto &enum_defs = static_cast(client_data)->enum_defs_; enum_defs[enum_name][variant_value] = variant_name; return CXChildVisit_Recurse; } if (clang_getCursorKind(parent) != CXCursor_StructDecl && clang_getCursorKind(parent) != CXCursor_UnionDecl) return CXChildVisit_Recurse; if (clang_getCursorKind(c) == CXCursor_FieldDecl) { auto &structs = static_cast(client_data)->structs; auto named_parent = get_named_parent(c); auto ptype = clang_getCanonicalType( clang_getCursorType(named_parent)); auto ptypestr = get_unqualified_type_name(ptype); auto ptypesize = clang_Type_getSizeOf(ptype); auto ident = get_clang_string(clang_getCursorSpelling(c)); auto offset = clang_Type_getOffsetOf(ptype, ident.c_str()) / 8; auto type = clang_getCanonicalType(clang_getCursorType(c)); auto sized_type = get_sized_type(type, structs); auto bitfield = getBitfield(c); bool is_data_loc = false; // Process field annotations auto annotation = get_field_decl_annotation(c); if (annotation) { if (*annotation == "tp_data_loc") { // If the field is a tracepoint __data_loc, we need to rewrite the // type as a u64. The reason is that the tracepoint infrastructure // exports an encoded 32bit integer that tells us where to find // the actual data and how wide it is. However, LLVM freaks out if // you try to cast a pointer to a u32 (rightfully so) so we need // this field to actually be 64 bits wide. sized_type = CreateInt64(); is_data_loc = true; } } // Initialize a new record type if needed if (!structs.Has(ptypestr)) 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 // duplicated types. structs.Lookup(ptypestr).lock()->AddField( ident, sized_type, offset, bitfield, is_data_loc); } return CXChildVisit_Recurse; }, &bpftrace); // clang_visitChildren returns a non-zero value if the traversal // was terminated by the visitor returning CXChildVisit_Break. return err == 0; } std::unordered_set ClangParser::get_incomplete_types() { if (input.empty()) return {}; // Parse without failing on compilation errors (ie incomplete structs) because // our goal is to enumerate all such errors. ClangParserHandler handler; if (!handler.parse_file("definitions.h", args, input_files, false)) return {}; struct TypeData { std::unordered_set complete_types; std::unordered_set incomplete_types; } type_data; CXCursor cursor = handler.get_translation_unit_cursor(); clang_visitChildren( cursor, [](CXCursor c, CXCursor parent, CXClientData client_data) { auto &data = *static_cast(client_data); // We look for field declarations and store the parent // as a fully defined type because we know we're looking at a // type definition. // // Then look at the field declaration itself. If it's a record // type (ie struct or union), check if we think it's a fully // defined type. If not, add it to incomplete types set. if (clang_getCursorKind(parent) == CXCursor_EnumDecl || (clang_getCursorKind(c) == CXCursor_FieldDecl && (clang_getCursorKind(parent) == CXCursor_UnionDecl || clang_getCursorKind(parent) == CXCursor_StructDecl))) { auto parent_type = clang_getCanonicalType( clang_getCursorType(parent)); data.complete_types.emplace(get_unqualified_type_name(parent_type)); auto cursor_type = clang_getCanonicalType(clang_getCursorType(c)); // We need layouts of pointee types because users could dereference if (cursor_type.kind == CXType_Pointer) cursor_type = clang_getPointeeType(cursor_type); if (cursor_type.kind == CXType_Record) { auto type_name = get_unqualified_type_name(cursor_type); if (data.complete_types.find(type_name) == data.complete_types.end()) data.incomplete_types.emplace(std::move(type_name)); } } return CXChildVisit_Recurse; }, &type_data); return type_data.incomplete_types; } void ClangParser::resolve_incomplete_types_from_btf( BPFtrace &bpftrace, const ast::ProbeList &probes) { // Resolution of incomplete types must run at least once, maximum should be // the number of levels of nested field accesses for tracepoint args. // The maximum number of iterations can be also controlled by the // BPFTRACE_MAX_TYPE_RES_ITERATIONS env variable (0 is unlimited). uint64_t field_lvl = 1; for (auto &probe : probes) if (probe->tp_args_structs_level > static_cast(field_lvl)) field_lvl = probe->tp_args_structs_level; unsigned max_iterations = std::max( bpftrace.config_.get(ConfigKeyInt::max_type_res_iterations), field_lvl); bool check_incomplete_types = true; for (unsigned i = 0; i < max_iterations && check_incomplete_types; i++) { // Collect incomplete types and retrieve their definitions from BTF. auto incomplete_types = get_incomplete_types(); size_t types_cnt = bpftrace.btf_set_.size(); bpftrace.btf_set_.insert(incomplete_types.cbegin(), incomplete_types.cend()); input_files.back() = get_btf_generated_header(bpftrace); // No need to continue if no more types were added check_incomplete_types = types_cnt != bpftrace.btf_set_.size(); } } // Parse the program using Clang. // // Type resolution rules: // // If BTF is available, necessary types are retrieved from there, otherwise we // rely on headers and types supplied by the user (we also include linux/types.h // in some cases, e.g., for tracepoints). // // The following types are taken from BTF (if available): // 1. Types explicitly used in the program (taken from bpftrace.btf_set_). // 2. Types used by some of the defined types (as struct members). This step // is done recursively, however, as it may take long time, there is a // maximal depth set. It is computed as the maximum level of nested field // accesses in the program and can be manually overridden using // the BPFTRACE_MAX_TYPE_RES_ITERATIONS env variable. // 3. Typedefs used by some of the defined types. These are also resolved // recursively, however, they must be resolved completely as any unknown // typedef will cause the parser to fail (even if the type is not used in // the program). // // If any of the above steps retrieves a definition that redefines some existing // (user-defined) type, no BTF types are used and all types must be provided. // In practice, this means that user may use kernel types without providing // their definitions but once he redefines any kernel type, he must provide all // necessary definitions. bool ClangParser::parse(ast::Program *program, BPFtrace &bpftrace, std::vector extra_flags) { #ifdef FUZZ StderrSilencer silencer; silencer.silence(); #endif input = "#include \n" + program->c_definitions; input_files = getTranslationUnitFiles(CXUnsavedFile{ .Filename = "definitions.h", .Contents = input.c_str(), .Length = input.size(), }); 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()); } // Push the generated BTF header into input files. // The header must be the last file in the vector since the following methods // count on it. // If BTF is not available, the header is empty. input_files.emplace_back(bpftrace.has_btf_data() ? get_btf_generated_header(bpftrace) : get_empty_btf_generated_header()); bool btf_conflict = false; ClangParserHandler handler; if (bpftrace.has_btf_data()) { // We set these args early because some systems may not have // (containers) and fully rely on BTF. // Prevent BTF generated header from redefining stuff found // in args.push_back("-D_LINUX_TYPES_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", args, input_files, false) && handler.has_redefinition_error()) btf_conflict = true; if (!btf_conflict) { resolve_incomplete_types_from_btf(bpftrace, program->probes); if (handler.parse_file("definitions.h", args, input_files, false) && handler.has_redefinition_error()) btf_conflict = true; } if (!btf_conflict) { resolve_unknown_typedefs_from_btf(bpftrace); if (handler.parse_file("definitions.h", args, input_files, false) && handler.has_redefinition_error()) btf_conflict = true; } } if (btf_conflict) { // There is a conflict (redefinition) between user-supplied types and types // taken from BTF. We cannot use BTF in such a case. args.pop_back(); args.pop_back(); input_files.back() = get_empty_btf_generated_header(); } if (!handler.parse_file("definitions.h", args, input_files)) { if (handler.has_redefinition_error()) { LOG(WARNING) << "Cannot take type definitions from BTF since there is " "a redefinition conflict with user-defined types."; } else if (handler.has_unknown_type_error()) { LOG(ERROR) << "Include headers with missing type definitions or install " "BTF information to your system."; if (bpftrace.btf_->objects_cnt() > 2) { LOG(WARNING) << "Trying to dump BTF from multiple kernel modules at once. " << "This is currently not possible, use probes from a single module" << " (and/or vmlinux) only."; } } return false; } CXCursor cursor = handler.get_translation_unit_cursor(); return visit_children(cursor, bpftrace); } // Parse the given Clang diagnostics message and if it has one of the forms: // unknown type name 'type_t' // use of undeclared identifier 'type_t' // return type_t. std::optional ClangParser::ClangParser::get_unknown_type( const std::string &diagnostic_msg) { const std::vector unknown_type_msgs = { "unknown type name \'", "use of undeclared identifier \'" }; for (auto &unknown_type_msg : unknown_type_msgs) { if (diagnostic_msg.find(unknown_type_msg) == 0) { return diagnostic_msg.substr(unknown_type_msg.length(), diagnostic_msg.length() - unknown_type_msg.length() - 1); } } return {}; } std::unordered_set ClangParser::get_unknown_typedefs() { // Parse without failing on compilation errors (ie unknown types) because // our goal is to enumerate and analyse all such errors ClangParserHandler handler; if (!handler.parse_file("definitions.h", args, input_files, false)) return {}; std::unordered_set unknown_typedefs; // Search for error messages of the form: // unknown type name 'type_t' // that imply an unresolved typedef of type_t. This cannot be done in // clang_visitChildren since clang does not have the unknown type names. for (const auto &msg : handler.get_error_messages()) { auto unknown_type = get_unknown_type(msg); if (unknown_type) unknown_typedefs.emplace(unknown_type.value()); } return unknown_typedefs; } void ClangParser::resolve_unknown_typedefs_from_btf(BPFtrace &bpftrace) { bool check_unknown_types = true; while (check_unknown_types) { // Collect unknown typedefs and retrieve their definitions from BTF. // These must be resolved completely since any unknown typedef will cause // the parser to fail (even if that type is not used in the program). auto incomplete_types = get_unknown_typedefs(); size_t types_cnt = bpftrace.btf_set_.size(); bpftrace.btf_set_.insert(incomplete_types.cbegin(), incomplete_types.cend()); input_files.back() = get_btf_generated_header(bpftrace); // No need to continue if no more types were added check_unknown_types = types_cnt != bpftrace.btf_set_.size(); } } CXUnsavedFile ClangParser::get_btf_generated_header(BPFtrace &bpftrace) { btf_cdef = bpftrace.btf_->c_def(bpftrace.btf_set_); return CXUnsavedFile{ .Filename = "/bpftrace/include/__btf_generated_header.h", .Contents = btf_cdef.c_str(), .Length = btf_cdef.size(), }; } CXUnsavedFile ClangParser::get_empty_btf_generated_header() { btf_cdef = ""; return CXUnsavedFile{ .Filename = "/bpftrace/include/__btf_generated_header.h", .Contents = btf_cdef.c_str(), .Length = btf_cdef.size(), }; } std::string ClangParser::get_arch_include_path() { struct utsname utsname; uname(&utsname); return "/usr/include/" + std::string(utsname.machine) + "-linux-gnu"; } static void query_clang_include_dirs(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 } } std::vector ClangParser::system_include_paths() { std::vector result; std::istringstream lines(SYSTEM_INCLUDE_PATHS); std::string line; while (std::getline(lines, line, ':')) { if (line == "auto") query_clang_include_dirs(result); else result.push_back(trim(line)); } if (result.empty()) result = { "/usr/local/include", "/usr/include" }; return result; } } // namespace bpftrace bpftrace-0.23.2/src/clang_parser.h000066400000000000000000000063121477746507000170070ustar00rootroot00000000000000#pragma once #include #include "bpftrace.h" #include namespace bpftrace { namespace ast { class Program; } class ClangParser { public: bool parse(ast::Program *program, BPFtrace &bpftrace, std::vector extra_flags = {}); private: bool visit_children(CXCursor &cursor, BPFtrace &bpftrace); // The user might have written some struct definitions that rely on types // supplied by BTF data. // // This method will pull out any forward-declared / incomplete struct // definitions and return the types (in string form) of the unresolved types. // // Note that this method does not report "errors". This is because the user // could have typo'd and actually referenced a non-existent type. Put // differently, this method is best effort. std::unordered_set get_incomplete_types(); // Iteratively check for incomplete types, pull their definitions from BTF, // and update the input files with the definitions. void resolve_incomplete_types_from_btf(BPFtrace &bpftrace, const ast::ProbeList &probes); // Collect names of types defined by typedefs that are in non-included // headers as they may pose problems for clang parser. std::unordered_set get_unknown_typedefs(); // Iteratively check for unknown typedefs, pull their definitions from BTF, // and update the input files with the definitions. void resolve_unknown_typedefs_from_btf(BPFtrace &bpftrace); static std::optional get_unknown_type( const std::string &diagnostic_msg); 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; std::string btf_cdef; class ClangParserHandler { public: ClangParserHandler(); ~ClangParserHandler(); bool parse_file(const std::string &filename, const std::vector &args, std::vector &unsaved_files, bool bail_on_errors = true); CXTranslationUnit get_translation_unit(); CXErrorCode parse_translation_unit(const char *source_filename, const char *const *command_line_args, int num_command_line_args, struct CXUnsavedFile *unsaved_files, unsigned num_unsaved_files, unsigned options); // Check diagnostics and collect all error messages. // Return true if an error occurred. If bail_on_error is false, only fail // on fatal errors. bool check_diagnostics(bool bail_on_error); CXCursor get_translation_unit_cursor(); const std::vector &get_error_messages(); bool has_redefinition_error(); bool has_unknown_type_error(); private: CXIndex index; CXTranslationUnit translation_unit = nullptr; std::vector error_msgs; }; }; } // namespace bpftrace bpftrace-0.23.2/src/config.cpp000066400000000000000000000140401477746507000161440ustar00rootroot00000000000000#include #include #include #include "config.h" #include "log.h" #include "types.h" namespace bpftrace { Config::Config(bool has_cmd) { config_map_ = { { ConfigKeyBool::cpp_demangle, { .value = true } }, { ConfigKeyBool::lazy_symbolication, { .value = false } }, { ConfigKeyBool::probe_inline, { .value = false } }, { ConfigKeyBool::print_maps_on_exit, { .value = true } }, #ifndef HAVE_BLAZESYM { ConfigKeyBool::use_blazesym, { .value = false } }, #else { ConfigKeyBool::use_blazesym, { .value = true } }, #endif { ConfigKeyInt::log_size, { .value = static_cast(1000000) } }, { ConfigKeyInt::max_bpf_progs, { .value = static_cast(1024) } }, { ConfigKeyInt::max_cat_bytes, { .value = static_cast(10240) } }, { ConfigKeyInt::max_map_keys, { .value = static_cast(4096) } }, { ConfigKeyInt::max_probes, { .value = static_cast(1024) } }, { ConfigKeyInt::max_strlen, { .value = static_cast(1024) } }, { ConfigKeyInt::max_type_res_iterations, { .value = static_cast(0) } }, { ConfigKeyInt::on_stack_limit, { .value = static_cast(32) } }, { ConfigKeyInt::perf_rb_pages, { .value = static_cast(64) } }, { ConfigKeyStackMode::default_, { .value = StackMode::bpftrace } }, { ConfigKeyString::str_trunc_trailer, { .value = std::string("..") } }, { ConfigKeySymbolSource::default_, { .value = #ifdef HAVE_LIBLLDB ConfigSymbolSource::dwarf #else ConfigSymbolSource::symbol_table #endif } }, { ConfigKeyMissingProbes::default_, { .value = ConfigMissingProbes::warn } }, // by default, cache user symbols per program if ASLR is disabled on system // or `-c` option is given { ConfigKeyUserSymbolCacheType::default_, { .value = (has_cmd || !is_aslr_enabled()) ? UserSymbolCacheType::per_program : UserSymbolCacheType::per_pid } }, }; } bool Config::can_set(ConfigSource prevSource, ConfigSource source) { if (prevSource == ConfigSource::default_ || (prevSource == ConfigSource::script && source == ConfigSource::env_var)) { return true; } return false; } // /proc/sys/kernel/randomize_va_space >= 1 bool Config::is_aslr_enabled() { std::string randomize_va_space_file = "/proc/sys/kernel/randomize_va_space"; { std::ifstream file(randomize_va_space_file); if (file.fail()) { LOG(V1) << std::strerror(errno) << ": " << randomize_va_space_file; // conservatively return true return true; } std::string line; if (std::getline(file, line) && std::stoi(line) < 1) return false; } return true; } std::map get_stack_mode_map() { std::map result; for (auto &mode : STACK_MODE_NAME_MAP) { result.emplace(mode.second, mode.first); } return result; } std::optional Config::get_stack_mode(const std::string &s) { static auto stack_mode_map = get_stack_mode_map(); auto found = stack_mode_map.find(s); if (found != stack_mode_map.end()) { return std::make_optional(found->second); } return std::nullopt; } std::optional Config::get_config_key(const std::string &str, std::string &err) { std::string maybe_key = str; static const std::string prefix = "bpftrace_"; std::transform(maybe_key.begin(), maybe_key.end(), maybe_key.begin(), [](unsigned char c) { return std::tolower(c); }); if (maybe_key.rfind(prefix, 0) == 0) { maybe_key = maybe_key.substr(prefix.length()); } if (ENV_ONLY.find(maybe_key) != ENV_ONLY.end()) { err = maybe_key + " can only be set as an environment variable"; return std::nullopt; } auto found = CONFIG_KEY_MAP.find(maybe_key); if (found == CONFIG_KEY_MAP.end()) { err = "Unrecognized config variable: " + str; return std::nullopt; } return std::make_optional(found->second); } bool ConfigSetter::set_stack_mode(const std::string &s) { auto stack_mode = Config::get_stack_mode(s); if (stack_mode.has_value()) return config_.set(ConfigKeyStackMode::default_, stack_mode.value(), source_); LOG(ERROR) << s << " is not a valid StackMode"; return false; } // Note: options 0 and 1 are for compatibility with older versions of bpftrace bool ConfigSetter::set_user_symbol_cache_type(const std::string &s) { UserSymbolCacheType usct; if (s == "PER_PID") { usct = UserSymbolCacheType::per_pid; } else if (s == "PER_PROGRAM") { usct = UserSymbolCacheType::per_program; } else if (s == "1") { // use the default return true; } else if (s == "NONE" || s == "0") { usct = UserSymbolCacheType::none; } else { LOG(ERROR) << "Invalid value for cache_user_symbols: valid values are " "PER_PID, PER_PROGRAM, and NONE."; return false; } return config_.set(ConfigKeyUserSymbolCacheType::default_, usct, source_); } bool ConfigSetter::set_symbol_source_config(const std::string &s) { ConfigSymbolSource source; if (s == "dwarf") { source = ConfigSymbolSource::dwarf; } else if (s == "symbol_table") { source = ConfigSymbolSource::symbol_table; } else { LOG(ERROR) << "Invalid value for symbol_source: valid values are " "\"dwarf\" and \"symbol_table\"."; return false; } return config_.set(ConfigKeySymbolSource::default_, source, source_); } bool ConfigSetter::set_missing_probes_config(const std::string &s) { ConfigMissingProbes mp; if (s == "ignore") { mp = ConfigMissingProbes::ignore; } else if (s == "warn") { mp = ConfigMissingProbes::warn; } else if (s == "error") { mp = ConfigMissingProbes::error; } else { LOG(ERROR) << "Invalid value for missing_probes: valid values are " "\"ignore\", \"warn\", and \"error\"."; return false; } return config_.set(ConfigKeyMissingProbes::default_, mp, source_); } } // namespace bpftrace bpftrace-0.23.2/src/config.h000066400000000000000000000137371477746507000156250ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include "types.h" namespace bpftrace { // This is used to determine which source // takes precedence when a config key is set enum class ConfigSource { // the default config value default_, // config set via environment variable env_var, // config set via config script syntax script, }; enum class ConfigKeyBool { cpp_demangle, lazy_symbolication, probe_inline, print_maps_on_exit, use_blazesym, }; enum class ConfigKeyInt { log_size, max_bpf_progs, max_cat_bytes, max_map_keys, max_probes, max_strlen, max_type_res_iterations, on_stack_limit, perf_rb_pages, }; enum class ConfigKeyString { str_trunc_trailer, }; enum class ConfigKeyStackMode { default_, }; enum class ConfigKeyUserSymbolCacheType { default_, }; enum class ConfigSymbolSource { symbol_table, dwarf, }; enum class ConfigKeySymbolSource { default_, }; enum class ConfigMissingProbes { ignore, warn, error, }; enum class ConfigKeyMissingProbes { default_, }; typedef std::variant ConfigKey; // The strings in CONFIG_KEY_MAP AND ENV_ONLY match the env variables (minus the // 'BPFTRACE_' prefix) const std::map CONFIG_KEY_MAP = { { "cache_user_symbols", ConfigKeyUserSymbolCacheType::default_ }, { "cpp_demangle", ConfigKeyBool::cpp_demangle }, { "lazy_symbolication", ConfigKeyBool::lazy_symbolication }, { "log_size", ConfigKeyInt::log_size }, { "max_bpf_progs", ConfigKeyInt::max_bpf_progs }, { "max_cat_bytes", ConfigKeyInt::max_cat_bytes }, { "max_map_keys", ConfigKeyInt::max_map_keys }, { "max_probes", ConfigKeyInt::max_probes }, { "max_strlen", ConfigKeyInt::max_strlen }, { "max_type_res_iterations", ConfigKeyInt::max_type_res_iterations }, { "on_stack_limit", ConfigKeyInt::on_stack_limit }, { "perf_rb_pages", ConfigKeyInt::perf_rb_pages }, { "probe_inline", ConfigKeyBool::probe_inline }, { "stack_mode", ConfigKeyStackMode::default_ }, { "str_trunc_trailer", ConfigKeyString::str_trunc_trailer }, { "symbol_source", ConfigKeySymbolSource::default_ }, { "missing_probes", ConfigKeyMissingProbes::default_ }, { "print_maps_on_exit", ConfigKeyBool::print_maps_on_exit }, { "use_blazesym", ConfigKeyBool::use_blazesym }, }; // These are not tracked by the config class const std::set ENV_ONLY = { "btf", "debug_output", "kernel_build", "kernel_source", "max_ast_nodes", "verify_llvm_ir", "vmlinux", }; struct ConfigValue { ConfigSource source = ConfigSource::default_; std::variant value; }; class Config { public: explicit Config(bool has_cmd = false); bool get(ConfigKeyBool key) const { return get(key); } uint64_t get(ConfigKeyInt key) const { return get(key); } std::string get(ConfigKeyString key) const { return get(key); } StackMode get(ConfigKeyStackMode key) const { return get(key); } UserSymbolCacheType get(ConfigKeyUserSymbolCacheType key) const { return get(key); } ConfigSymbolSource get(ConfigKeySymbolSource key) const { return get(key); } ConfigMissingProbes get(ConfigKeyMissingProbes key) const { return get(key); } static std::optional get_stack_mode(const std::string &s); std::optional get_config_key(const std::string &str, std::string &err); friend class ConfigSetter; private: template bool set(ConfigKey key, T val, ConfigSource source) { auto it = config_map_.find(key); if (it == config_map_.end()) { throw std::runtime_error("No default set for config key"); } if (!can_set(it->second.source, source)) { return false; } it->second.value = val; it->second.source = source; return true; } template T get(ConfigKey key) const { auto it = config_map_.find(key); if (it == config_map_.end()) { throw std::runtime_error("Config key does not exist in map"); } try { return std::get(it->second.value); } catch (std::bad_variant_access const &ex) { // This shouldn't happen throw std::runtime_error("Type mismatch for config key"); } } private: bool can_set(ConfigSource prevSource, ConfigSource); bool is_aslr_enabled(); std::map config_map_; }; class ConfigSetter { public: explicit ConfigSetter(Config &config, ConfigSource source) : config_(config), source_(source) {}; bool set(ConfigKeyBool key, bool val) { return config_.set(key, val, source_); } bool set(ConfigKeyInt key, uint64_t val) { return config_.set(key, val, source_); } bool set(ConfigKeyString key, const std::string &val) { return config_.set(key, val, source_); } bool set(StackMode val) { return config_.set(ConfigKeyStackMode::default_, val, source_); } bool set(UserSymbolCacheType val) { return config_.set(ConfigKeyUserSymbolCacheType::default_, val, source_); } bool set(ConfigMissingProbes val) { return config_.set(ConfigKeyMissingProbes::default_, val, source_); } bool set_stack_mode(const std::string &s); bool set_user_symbol_cache_type(const std::string &s); bool set_symbol_source_config(const std::string &s); bool set_missing_probes_config(const std::string &s); Config &config_; private: const ConfigSource source_; }; } // namespace bpftrace bpftrace-0.23.2/src/container/000077500000000000000000000000001477746507000161565ustar00rootroot00000000000000bpftrace-0.23.2/src/container/cstring_view.h000066400000000000000000000021131477746507000210270ustar00rootroot00000000000000#pragma once #include #include namespace bpftrace { // cstring_view // // A restricted version of std::string_view which guarantees that the underlying // string buffer will be null-terminated. This can be useful when interacting // with C APIs while avoiding the use of char* and unnecessary copies from using // std::string. // // We only allow constructing cstring_view from types which are guaranteed to // store null-terminated strings. All modifiers or operations on cstring_view // will also maintain the null-terminated property. class cstring_view : public std::string_view { public: constexpr cstring_view(const char *str) noexcept : std::string_view{ str } { } constexpr cstring_view(const std::string &str) noexcept : std::string_view{ str } { } constexpr const char *c_str() const noexcept { return data(); } private: // Disallow use of functions which can break the null-termination invariant using std::string_view::copy; using std::string_view::remove_suffix; using std::string_view::substr; }; } // namespace bpftrace bpftrace-0.23.2/src/cxxdemangler/000077500000000000000000000000001477746507000166555ustar00rootroot00000000000000bpftrace-0.23.2/src/cxxdemangler/CMakeLists.txt000066400000000000000000000001741477746507000214170ustar00rootroot00000000000000add_library(cxxdemangler_stdlib STATIC cxxdemangler_stdlib.cpp) add_library(cxxdemangler_llvm STATIC cxxdemangler_llvm.cpp) bpftrace-0.23.2/src/cxxdemangler/cxxdemangler.h000066400000000000000000000002511477746507000215050ustar00rootroot00000000000000#pragma once namespace bpftrace { // Demangle a mangled C++ symbol name // // Note: callee `free()`ed char* cxxdemangle(const char* mangled); } // namespace bpftrace bpftrace-0.23.2/src/cxxdemangler/cxxdemangler_llvm.cpp000066400000000000000000000005261477746507000230770ustar00rootroot00000000000000#include "cxxdemangler.h" #include #include namespace bpftrace { char* cxxdemangle(const char* mangled) { #if LLVM_VERSION_MAJOR <= 16 return llvm::itaniumDemangle(mangled, nullptr, nullptr, nullptr); #else return llvm::itaniumDemangle(mangled); #endif } } // namespace bpftrace bpftrace-0.23.2/src/cxxdemangler/cxxdemangler_stdlib.cpp000066400000000000000000000003141477746507000234010ustar00rootroot00000000000000#include "cxxdemangler.h" #include namespace bpftrace { char* cxxdemangle(const char* mangled) { return abi::__cxa_demangle(mangled, nullptr, nullptr, nullptr); } } // namespace bpftrace bpftrace-0.23.2/src/debugfs.cpp000066400000000000000000000004061477746507000163170ustar00rootroot00000000000000#include "debugfs.h" #include namespace bpftrace::debugfs { #define DEBUGFS "/sys/kernel/debug" std::string path() { return DEBUGFS; } std::string path(const std::string &file) { return path() + "/" + file; } } // namespace bpftrace::debugfs bpftrace-0.23.2/src/debugfs.h000066400000000000000000000004131477746507000157620ustar00rootroot00000000000000#pragma once #include namespace bpftrace { namespace debugfs { std::string path(); std::string path(const std::string &file); inline std::string kprobes_blacklist() { return path("kprobes/blacklist"); } } // namespace debugfs } // namespace bpftrace bpftrace-0.23.2/src/disasm.cpp000066400000000000000000000007561477746507000161700ustar00rootroot00000000000000#include "disasm.h" #include "bfd-disasm.h" namespace bpftrace { class DummyDisasm : public IDisasm { AlignState is_aligned(uint64_t offset __attribute__((unused)), uint64_t pc __attribute__((unused))) override { return AlignState::NotSupp; } }; Disasm::Disasm(std::string &path __attribute__((unused))) { #ifdef HAVE_BFD_DISASM dasm_ = std::make_unique(path); #else dasm_ = std::make_unique(); #endif } } // namespace bpftrace bpftrace-0.23.2/src/disasm.h000066400000000000000000000007701477746507000156310ustar00rootroot00000000000000#pragma once #include #include #include namespace bpftrace { enum class AlignState { Ok, Fail, NotAlign, NotSupp }; class IDisasm { public: virtual AlignState is_aligned(uint64_t offset, uint64_t pc) = 0; virtual ~IDisasm() = default; }; class Disasm { public: Disasm(std::string &path); AlignState is_aligned(uint64_t offset, uint64_t pc) { return dasm_->is_aligned(offset, pc); } private: std::unique_ptr dasm_; }; } // namespace bpftrace bpftrace-0.23.2/src/driver.cpp000066400000000000000000000073371477746507000162050ustar00rootroot00000000000000#include #include "ast/attachpoint_parser.h" #include "driver.h" #include "log.h" #include "parser.tab.hh" struct yy_buffer_state; extern struct yy_buffer_state *yy_scan_string(const char *yy_str, yyscan_t yyscanner); extern int yylex_init(yyscan_t *scanner); extern int yylex_destroy(yyscan_t yyscanner); extern bpftrace::location loc; namespace bpftrace { Driver::Driver(BPFtrace &bpftrace, std::ostream &o) : bpftrace_(bpftrace), out_(o) { } void Driver::source(std::string_view filename, std::string_view script) { Log::get().set_source(filename, script); } // Kept for the test suite int Driver::parse_str(std::string_view script) { source("stdin", script); return parse(); } int Driver::parse() { // Reset previous state if we parse more than once ctx = {}; // Reset source location info on every pass loc.initialize(); yyscan_t scanner; yylex_init(&scanner); Parser parser(*this, scanner); if (debug_) { parser.set_debug_level(1); } yy_scan_string(Log::get().get_source().c_str(), scanner); parser.parse(); yylex_destroy(scanner); if (!failed_) { ast::AttachPointParser ap_parser(ctx, bpftrace_, out_, listing_); if (ap_parser.parse()) failed_ = true; } if (failed_) { ctx = {}; } // Before proceeding, ensure that the size of the AST isn't past prescribed // limits. This functionality goes back to 80642a994, where it was added in // order to prevent stack overflow during fuzzing. It traveled through the // passes and visitor pattern, and this is a final return to the simplest // possible form. It is not necessary to walk the full AST in order to // determine the number of nodes. This can be done before any passes. auto node_count = ctx.node_count(); if (node_count > bpftrace_.max_ast_nodes_) { LOG(ERROR, out_) << "node count (" << node_count << ") exceeds the limit (" << bpftrace_.max_ast_nodes_ << ")"; failed_ = true; } // Keep track of errors thrown ourselves, since the result of // parser_->parse() doesn't take scanner errors into account, // only parser errors. return failed_; } void Driver::error(const location &l, const std::string &m) { LOG(ERROR, l, out_) << m; failed_ = true; } void Driver::error(const std::string &m) { LOG(ERROR, out_) << m; failed_ = true; } // Retrieves the list of kernel modules for all attachpoints. Will be used to // identify modules whose BTF we need to parse. // Currently, this is useful for fentry/fexit, k(ret)probes, and tracepoints. std::set Driver::list_modules() const { std::set modules; for (auto &probe : ctx.root->probes) { for (auto &ap : probe->attach_points) { auto probe_type = probetype(ap->provider); if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit || ((probe_type == ProbeType::kprobe || probe_type == ProbeType::kretprobe) && !ap->target.empty())) { if (ap->expansion != ast::ExpansionType::NONE) { for (auto &match : bpftrace_.probe_matcher_->get_matches_for_ap(*ap)) { std::string func = match; erase_prefix(func); auto match_modules = bpftrace_.get_func_modules(func); modules.insert(match_modules.begin(), match_modules.end()); } } else modules.insert(ap->target); } else if (probe_type == ProbeType::tracepoint) { // For now, we support this for a single target only since tracepoints // need dumping of C definitions BTF and that is not available for // multiple modules at once. modules.insert(ap->target); } } } return modules; } } // namespace bpftrace bpftrace-0.23.2/src/driver.h000066400000000000000000000014131477746507000156370ustar00rootroot00000000000000#pragma once #include #include #include "ast/ast.h" #include "bpftrace.h" typedef void *yyscan_t; namespace bpftrace { class Driver { public: explicit Driver(BPFtrace &bpftrace, std::ostream &o = std::cerr); int parse(); int parse_str(std::string_view script); void source(std::string_view, std::string_view); 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::ASTContext ctx; void debug() { debug_ = true; }; std::set list_modules() const; BPFtrace &bpftrace_; bool listing_ = false; private: std::ostream &out_; bool failed_ = false; bool debug_ = false; }; } // namespace bpftrace bpftrace-0.23.2/src/dwarf_parser.cpp000066400000000000000000000234701477746507000173650ustar00rootroot00000000000000#include "dwarf_parser.h" #ifdef HAVE_LIBLLDB #include "bpftrace.h" #include "log.h" #include "types.h" #include #include #include #include #include #include namespace bpftrace { std::atomic Dwarf::InstanceCounter::count = 0; Dwarf::InstanceCounter::InstanceCounter() { if (count++ == 0) lldb::SBDebugger::Initialize(); } Dwarf::InstanceCounter::~InstanceCounter() { if (--count == 0) lldb::SBDebugger::Terminate(); } Dwarf::Dwarf(BPFtrace *bpftrace, std::string file_path) : counter_(), bpftrace_(bpftrace), file_path_(std::move(file_path)) { // Create a "hardened" debugger instance with no scripting, nor .lldbinit. // We don't want a Python extension or .lldbinit to influence the byte-code // that will get executed by the Kernel. It would be a security risk! debugger_ = lldb::SBDebugger::Create(/* source_init_file = */ false); debugger_.SetScriptLanguage(lldb::ScriptLanguage::eScriptLanguageNone); lldb::SBError error; target_ = debugger_.CreateTarget( file_path_.c_str(), nullptr, nullptr, true, error); if (!error.Success() || !target_.IsValid()) { throw error; } } bool Dwarf::has_debug_info() { // Verify that the target contains DebugInfo. if (auto stats = target_.GetStatistics()) if (auto modules = stats.GetValueForKey("modules")) if (auto module = modules.GetItemAtIndex(0)) if (auto hasDebugInfo = module.GetValueForKey("debugInfoByteSize")) if (hasDebugInfo.GetIntegerValue() > 0) return true; return false; } std::unique_ptr Dwarf::GetFromBinary(BPFtrace *bpftrace, std::string file_path) { try { // Can't use std::make_unique because Dwarf's constructor is private return std::unique_ptr(new Dwarf(bpftrace, std::move(file_path))); } catch (const lldb::SBError &error) { LOG(ERROR) << "Failed to parse DebugInfo: " << error.GetCString(); return nullptr; } } std::vector Dwarf::get_function_locations(const std::string &function, bool include_inlined) { // Locating every inlined instances of a function is expensive, // so we only do it if the user explicitly requests it. if (!include_inlined) { auto syms = target_.FindSymbols(function.c_str(), lldb::SymbolType::eSymbolTypeCode); // The given function name MUST identify a unique symbol! if (syms.GetSize() != 1) return {}; auto sym = syms.GetContextAtIndex(0).GetSymbol(); return { sym.GetStartAddress().GetFileAddress() + sym.GetPrologueByteSize() }; } else { auto bps = target_.BreakpointCreateByName(function.c_str()); std::vector result(bps.GetNumLocations()); for (uint32_t i = 0; i < bps.GetNumLocations(); i++) { auto loc = bps.GetLocationAtIndex(i); result[i] = loc.GetAddress().GetFileAddress(); } return result; } } std::string Dwarf::get_type_name(lldb::SBType type) { std::string type_name = type.GetDisplayTypeName() ?: ""; // Get Pointee type to add the struct/union/enum prefix for C while (type.IsPointerType()) type = type.GetPointeeType(); switch (type.GetTypeClass()) { case lldb::eTypeClassStruct: return "struct " + type_name; case lldb::eTypeClassUnion: return "union " + type_name; case lldb::eTypeClassEnumeration: return "enum " + type_name; default: return type_name; } } lldb::SBValueList Dwarf::function_params(const std::string &function) { auto functions = target_.FindFunctions(function.c_str()); // The given function name MUST identify a unique function! if (functions.GetSize() != 1) return lldb::SBValueList(); auto fn = functions.GetContextAtIndex(0).GetFunction(); return fn.GetBlock().GetVariables( target_, /*arguments=*/true, /*locals=*/false, /*statics=*/false); } std::vector Dwarf::get_function_params(const std::string &function) { auto params = function_params(function); std::vector result; result.reserve(params.GetSize()); for (uint32_t i = 0; i < params.GetSize(); i++) { auto param = params.GetValueAtIndex(i); std::string param_name = param.GetName() ?: ""; std::string param_type_name = get_type_name(param.GetType()); result.push_back(param_type_name + (param_name.empty() ? "" : " " + param_name)); } return result; } Struct Dwarf::resolve_args(const std::string &function) { auto params = function_params(function); Struct result(0, false); for (uint32_t i = 0; i < params.GetSize(); i++) { auto param = params.GetValueAtIndex(i); auto name = param.GetName() ?: ""; auto arg_type = get_stype(param.GetType()); arg_type.is_funcarg = true; arg_type.funcarg_idx = i; result.AddField(name, arg_type, result.size, std::nullopt, false); result.size += arg_type.GetSize(); } return result; } SizedType Dwarf::get_stype(lldb::SBType type, bool resolve_structs) { if (!type.IsValid()) return CreateNone(); auto bit_size = 8 * type.GetByteSize(); switch (type.GetTypeClass()) { case lldb::eTypeClassBuiltin: { switch (type.GetBasicType()) { case lldb::eBasicTypeBool: case lldb::eBasicTypeChar: case lldb::eBasicTypeSignedChar: case lldb::eBasicTypeWChar: case lldb::eBasicTypeSignedWChar: case lldb::eBasicTypeChar8: case lldb::eBasicTypeChar16: case lldb::eBasicTypeChar32: case lldb::eBasicTypeShort: case lldb::eBasicTypeInt: case lldb::eBasicTypeInt128: case lldb::eBasicTypeLong: case lldb::eBasicTypeLongLong: return CreateInt(bit_size); case lldb::eBasicTypeUnsignedChar: case lldb::eBasicTypeUnsignedWChar: case lldb::eBasicTypeUnsignedShort: case lldb::eBasicTypeUnsignedInt: case lldb::eBasicTypeUnsignedInt128: case lldb::eBasicTypeUnsignedLong: case lldb::eBasicTypeUnsignedLongLong: return CreateUInt(bit_size); default: return CreateNone(); } } case lldb::eTypeClassEnumeration: return CreateUInt(bit_size); case lldb::eTypeClassPointer: return CreatePointer(get_stype(type.GetPointeeType(), false)); case lldb::eTypeClassReference: return CreateReference(get_stype(type.GetDereferencedType(), true)); case lldb::eTypeClassClass: case lldb::eTypeClassStruct: case lldb::eTypeClassUnion: { auto name = get_type_name(type); auto str = bpftrace_->structs.LookupOrAdd(name, bit_size / 8); if (resolve_structs) resolve_fields(str.lock(), type); return CreateRecord(name, str); } case lldb::eTypeClassArray: { auto inner_type = type.GetArrayElementType(); auto inner_stype = get_stype(inner_type); // Create a fake array instance to get its length auto val = target_.CreateValueFromData("__field", lldb::SBData(), type); auto length = val.GetNumChildren(); switch (inner_type.GetBasicType()) { case lldb::eBasicTypeChar: case lldb::eBasicTypeSignedChar: case lldb::eBasicTypeUnsignedChar: case lldb::eBasicTypeChar8: return CreateString(length); default: return CreateArray(length, inner_stype); } } case lldb::eTypeClassTypedef: return get_stype(type.GetTypedefedType(), resolve_structs); default: return CreateNone(); } } SizedType Dwarf::get_stype(const std::string &type_name) { if (auto type = target_.FindFirstType(type_name.c_str())) return get_stype(type); return CreateNone(); } struct Subobject { lldb::SBType type; size_t offset; }; void Dwarf::resolve_fields(const SizedType &type) { if (!type.IsRecordTy()) return; auto type_name = type.GetName(); auto str = bpftrace_->structs.Lookup(type_name).lock(); if (str->HasFields()) return; auto type_dbg = target_.FindFirstType(type_name.c_str()); if (!type_dbg.IsValid()) return; resolve_fields(std::move(str), std::move(type_dbg)); } void Dwarf::resolve_fields(std::shared_ptr str, lldb::SBType type) { if (!str || str->HasFields()) return; if (!type.IsValid()) return; std::queue subobjects{ std::deque{ Subobject{ std::move(type), 0 } } }; while (!subobjects.empty()) { auto &subobject = subobjects.front(); // Collect the fields into str, duplicates will be ignored. for (uint32_t i = 0; i < subobject.type.GetNumberOfFields(); i++) { auto field = subobject.type.GetFieldAtIndex(i); str->AddField(field.GetName() ?: "", get_stype(field.GetType()), subobject.offset + field.GetOffsetInBytes(), resolve_bitfield(field), false); } // Queue the bases for further processing. for (uint32_t i = 0; i < subobject.type.GetNumberOfDirectBaseClasses(); i++) { auto base = subobject.type.GetDirectBaseClassAtIndex(i); subobjects.push(Subobject{ base.GetType(), subobject.offset + base.GetOffsetInBytes() }); } subobjects.pop(); } // Order the fields for consistent output when printing record types. std::sort(str->fields.begin(), str->fields.end(), [](const Field &a, const Field &b) { return a.offset < b.offset || (a.offset == b.offset && a.name < b.name); }); } std::optional Dwarf::resolve_bitfield(lldb::SBTypeMember field) { if (!field.IsBitfield()) return std::nullopt; auto bit_offset = field.GetOffsetInBits(); auto bitfield_width = field.GetBitfieldSizeInBits(); return Bitfield(bit_offset % 8, bitfield_width); } } // namespace bpftrace #endif // HAVE_LIBLLDB bpftrace-0.23.2/src/dwarf_parser.h000066400000000000000000000054701477746507000170320ustar00rootroot00000000000000#pragma once #include "struct.h" #include "types.h" #include #include #include #include #ifdef HAVE_LIBLLDB #include #include namespace bpftrace { class BPFtrace; class Dwarf { public: static std::unique_ptr GetFromBinary(BPFtrace *bpftrace, std::string file_path); bool has_debug_info(); std::vector get_function_locations(const std::string &function, bool include_inlined); std::vector get_function_params(const std::string &function); Struct resolve_args(const std::string &function); SizedType get_stype(const std::string &type_name); void resolve_fields(const SizedType &type); private: Dwarf(BPFtrace *bpftrace, std::string file_path); lldb::SBValueList function_params(const std::string &function); std::string get_type_name(lldb::SBType type); SizedType get_stype(lldb::SBType type, bool resolve_structs = true); void resolve_fields(std::shared_ptr str, lldb::SBType type); std::optional resolve_bitfield(lldb::SBTypeMember field); // Initialize/Terminate liblldb globally with RAII class InstanceCounter final { private: static std::atomic count; public: InstanceCounter(); ~InstanceCounter(); }; InstanceCounter counter_; BPFtrace *bpftrace_; std::string file_path_; lldb::SBDebugger debugger_; lldb::SBTarget target_; }; } // namespace bpftrace #else // HAVE_LIBLLDB #include "log.h" namespace bpftrace { class BPFtrace; class Dwarf { public: static std::unique_ptr GetFromBinary(BPFtrace *bpftrace __attribute__((unused)), std::string file_path_ __attribute__((unused))) { return nullptr; } bool has_debug_info() { return false; } std::vector get_function_locations(const std::string &function __attribute__((unused)), bool include_inlined __attribute__((unused))) { return {}; } std::vector get_function_params(const std::string &function __attribute__((unused))) { return {}; } Struct resolve_args(const std::string &function __attribute__((unused))) { return {}; } SizedType get_stype(const std::string &type_name __attribute__((unused))) { return CreateNone(); } void resolve_fields(const SizedType &type __attribute__((unused))) { } private: Dwarf() = delete; }; } // namespace bpftrace #endif // HAVE_LIBLLDB bpftrace-0.23.2/src/format_string.cpp000066400000000000000000000205601477746507000175610ustar00rootroot00000000000000#include "format_string.h" #include "log.h" #include "struct.h" #include "utils.h" #include #include #include namespace bpftrace { const int FMT_BUF_SZ = 512; // bpf_trace_printk cannot use more than three arguments, see bpf-helpers(7). const int PRINTK_MAX_ARGS = 3; namespace { const std::regex length_modifier_re("%-?[0-9.]*(hh|h|l|ll|j|z|t)?([cduoxXp])"); const std::unordered_map length_modifier_type = { { "hh", ArgumentType::CHAR }, { "h", ArgumentType::SHORT }, { "", ArgumentType::INT }, { "l", ArgumentType::LONG }, { "ll", ArgumentType::LONG_LONG }, { "j", ArgumentType::INTMAX_T }, { "z", ArgumentType::SIZE_T }, { "t", ArgumentType::PTRDIFF_T }, }; ArgumentType get_expected_argument_type(const std::string &fmt) { std::smatch match; if (std::regex_search(fmt, match, length_modifier_re)) { if (match[2] == "p") return ArgumentType::POINTER; else if (match[2] == "c") return ArgumentType::CHAR; auto it = length_modifier_type.find(match[1]); if (it != length_modifier_type.end()) return it->second; } return ArgumentType::UNKNOWN; } // Returns a vector of (token, type) tuples given a format string std::vector> get_token_types( const std::string &fmt, const std::unordered_map &format_types) { std::vector> types; auto tokens_begin = std::sregex_iterator(fmt.begin(), fmt.end(), format_specifier_re); auto tokens_end = std::sregex_iterator(); for (auto iter = tokens_begin; iter != tokens_end; iter++) { int offset = 1; // skip over format widths during verification if (iter->str()[offset] == '-') offset++; while ((iter->str()[offset] >= '0' && iter->str()[offset] <= '9') || iter->str()[offset] == '.') offset++; std::string token = iter->str().substr(offset); auto token_type = Type::none; if (format_types.contains(token)) token_type = format_types.at(token); types.push_back(std::make_tuple(token, token_type)); } return types; } } // anonymous namespace std::string validate_format_string(const std::string &fmt, std::vector args, const std::string call_func) { std::stringstream message; const auto &format_types = call_func == "debugf" ? bpf_trace_printk_format_types : printf_format_types; auto token_types = get_token_types(fmt, format_types); int num_tokens = token_types.size(); int num_args = args.size(); if (num_args < num_tokens) { message << call_func << ": Not enough arguments for format string (" << num_args << " supplied, " << num_tokens << " expected)" << std::endl; return message.str(); } if (num_args > num_tokens) { message << call_func << ": Too many arguments for format string (" << num_args << " supplied, " << num_tokens << " expected)" << std::endl; return message.str(); } if (call_func == "debugf" && num_args > PRINTK_MAX_ARGS) { message << call_func << ": Cannot use more than " << PRINTK_MAX_ARGS << " conversion specifiers" << std::endl; return message.str(); } for (int i = 0; i < num_args; i++) { std::unordered_set arg_types; Type arg_type = args.at(i).type.GetTy(); if (arg_type == Type::ksym_t || arg_type == Type::usym_t || arg_type == Type::username || arg_type == Type::kstack_t || arg_type == Type::ustack_t || arg_type == Type::inet || arg_type == Type::timestamp || arg_type == Type::mac_address || arg_type == Type::cgroup_path_t || arg_type == Type::strerror_t) { arg_types.insert(Type::string); // Symbols should be printed as // strings } else if (arg_type == Type::pointer) { arg_types.insert(Type::integer); // Casts (pointers) can be // printed as integers } else if (args.at(i).type.IsEnumTy()) { // Symbolizing enum arg_types.insert(Type::string); arg_types.insert(Type::integer); } else { arg_types.insert(arg_type); } auto token = std::get<0>(token_types[i]); auto token_type = std::get<1>(token_types[i]); if (token_type == Type::none) { message << call_func << ": Unknown format string token: %" << token << std::endl; return message.str(); } if (!arg_types.contains(token_type)) { std::vector arg_types_vec; for (const auto &ty : arg_types) arg_types_vec.push_back(to_string(ty)); message << call_func << ": %" << token << " specifier expects a value of type " << token_type << " (" << str_join(arg_types_vec, ",") << " 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(); // figure out the argument type for each format specifier expected_types_.resize(parts_.size()); for (size_t i = 0; i < parts_.size(); i++) expected_types_[i] = get_expected_argument_type(parts_[i]); // Note we're passing in the superset `printf_format_types` regardless // of what the calling context was. This is ok b/c the format string // was already validated for correctness during compilation. tokens_ = get_token_types(fmt_, printf_format_types); } auto buffer = std::vector(FMT_BUF_SZ); auto check_snprintf_ret = [](int r) { if (r < 0) { char *e = std::strerror(errno); throw FatalUserException("format() error occurred: " + std::string(e ? e : "")); } }; 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" || fmt_string == "%rh") { if (fmt_string == "%rx" || fmt_string == "%rh") { auto printable_buffer = dynamic_cast(&*args.at(i)); // this is checked by semantic analyzer assert(printable_buffer); printable_buffer->keep_ascii(false); if (fmt_string == "%rh") printable_buffer->escape_hex(false); } // replace nonstandard format specifier with %s printf_fmt = std::regex_replace(parts_[i], std::regex("%r[x|h]?"), "%s"); } else { printf_fmt = parts_[i]; } int r = args.at(i)->print(buffer.data(), buffer.capacity(), printf_fmt.c_str(), std::get<1>(tokens_[i]), expected_types_[i]); 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 bpftrace-0.23.2/src/format_string.h000066400000000000000000000041671477746507000172330ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "printf.h" #include "types.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, const std::string call_func); 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_; std::vector expected_types_; std::vector> tokens_; friend class cereal::access; template void serialize(Archive &ar) { // NOTE: parts_, expected_types_, and tokens_ are not constructed until // first use, so no point in serializing them ar(fmt_); } }; } // namespace bpftrace bpftrace-0.23.2/src/functions.cpp000066400000000000000000000112531477746507000167120ustar00rootroot00000000000000#include "functions.h" #include "log.h" namespace bpftrace { namespace { std::string arg_types_str(const std::vector &arg_types) { std::string str = "("; bool first = true; for (const SizedType &arg_type : arg_types) { if (!first) str += ", "; str += typestr(arg_type); first = false; } str += ")"; return str; } std::string param_types_str(const std::vector ¶ms) { std::string str = "("; bool first = true; for (const Param ¶m : params) { if (!first) str += ", "; str += typestr(param.type()); first = false; } str += ")"; return str; } } // namespace const Function *FunctionRegistry::add(Function::Origin origin, std::string_view name, const SizedType &return_type, const std::vector ¶ms) { return add(origin, {}, name, return_type, params); } const Function *FunctionRegistry::add(Function::Origin origin, std::string_view ns, std::string_view name, const SizedType &return_type, const std::vector ¶ms) { FqName fq_name{ .ns = std::string{ ns }, .name = std::string{ name }, }; // Check for duplicate function definitions // The assumption is that builtin functions are all added to the registry // before any user-defined functions. // Builtin functions can be duplicated. Other functions can not. for (const Function &func : funcs_by_fq_name_[fq_name]) { if (func.origin() != Function::Origin::Builtin) { return nullptr; } } all_funcs_.push_back(std::make_unique( origin, std::string{ name }, return_type, params)); Function &new_func = *all_funcs_.back().get(); funcs_by_fq_name_[fq_name].push_back(new_func); return &new_func; } namespace { bool can_implicit_cast(const SizedType &from, const SizedType &to) { if (from.FitsInto(to)) return true; if (from.IsStringTy() && to.IsPtrTy() && to.GetPointeeTy()->IsIntTy() && to.GetPointeeTy()->GetSize() == 1) { // Allow casting from string to int8* or uint8* return true; } // Builtin and script functions do not care about string sizes. External // functions cannot be defined to accept string types (they'd take char*) if (from.IsStringTy() && to.IsStringTy()) return true; return false; } } // namespace // Find the best function by name for the given argument types. // // Returns either a single function or nullptr, when no such function exists. // // When there are multiple candidate functions with the same name, prefer the // non-builtin over the builtin function. // // Valid functions have the correct name and all arguments can be implicitly // casted into all parameter types. const Function *FunctionRegistry::get(std::string_view ns, std::string_view name, const std::vector &arg_types, std::ostream &out, std::optional loc) const { FqName fq_name = { .ns = std::string{ ns }, .name = std::string{ name }, }; auto it = funcs_by_fq_name_.find(fq_name); if (it == funcs_by_fq_name_.end()) { LOG(ERROR, loc, out) << "Function not found: '" << name << "'"; return nullptr; } const auto &candidates = it->second; // We disallow duplicate functions other than for builtins, so expect at most // two exact matches. assert(candidates.size() <= 2); // No candidates => no match // 1 candidate => use it // 2 candidates => use non-builtin candidate const Function *candidate = nullptr; for (const Function &func : candidates) { candidate = &func; if (candidate->origin() != Function::Origin::Builtin) break; } // Validate that the candidate's parameters can take our arguments if (candidate) { bool valid = true; if (candidate->params().size() != arg_types.size()) { valid = false; } else { for (size_t i = 0; i < arg_types.size(); i++) { if (!can_implicit_cast(arg_types[i], candidate->params()[i].type())) { valid = false; break; } } } if (valid) return candidate; } LOG(ERROR, loc, out) << "Cannot call function '" << name << "' using argument types: " << arg_types_str(arg_types); LOG(HINT, out) << "Candidate function:\n " << candidate->name() << param_types_str(candidate->params()); return nullptr; } } // namespace bpftrace bpftrace-0.23.2/src/functions.h000066400000000000000000000063601477746507000163620ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "location.hh" #include "types.h" namespace bpftrace { // A parameter for a BpfScript function class Param { public: Param(std::string name, const SizedType &type) : name_(std::move(name)), type_(type) { } const std::string &name() const { return name_; } const SizedType &type() const { return type_; } private: std::string name_; SizedType type_; }; // Represents the type of a function which is callable in a BpfScript program. // // The function's implementation is not contained here. class Function { public: // "Builtin" functions are hardcoded into bpftrace. // "Script" functions are user-defined in BpfScript. // "External" functions are imported from pre-compiled BPF programs. enum class Origin { Builtin, Script, External, }; Function(Origin origin, std::string name, const SizedType &return_type, const std::vector ¶ms) : name_(std::move(name)), return_type_(return_type), params_(params), origin_(origin) { } const std::string &name() const { return name_; } const SizedType &return_type() const { return return_type_; } const std::vector ¶ms() const { return params_; } Origin origin() const { return origin_; } private: std::string name_; SizedType return_type_; std::vector params_; Origin origin_; }; // Registry of callable functions // // Non-builtin functions are not allowed to share the same name. When a builtin // and a non-builtin function share a name, the non-builtin is preferred. class FunctionRegistry { public: const Function *add(Function::Origin origin, std::string_view name, const SizedType &return_type, const std::vector ¶ms); const Function *add(Function::Origin origin, std::string_view ns, std::string_view name, const SizedType &return_type, const std::vector ¶ms); // Returns the best match for the given function name and arguments const Function *get(std::string_view ns, std::string_view name, const std::vector &arg_types, std::ostream &out = std::cerr, std::optional loc = {}) const; private: struct FqName { std::string ns; std::string name; std::string str() const { if (ns.empty()) return name; return ns + "::" + name; } bool operator==(const FqName &other) const { return ns == other.ns && name == other.name; } }; class HashFqName { public: size_t operator()(const FqName &fq_name) const { return std::hash()(fq_name.ns) ^ std::hash()(fq_name.name); } }; std::unordered_map>, HashFqName> funcs_by_fq_name_; std::vector> all_funcs_; }; } // namespace bpftrace bpftrace-0.23.2/src/fuzz_main.cpp000066400000000000000000000072621477746507000167110ustar00rootroot00000000000000// main function for fuzzing // To build this, add -DBUILD_FUZZ=1 CMake option // The compiled binary name is "bpftrace_fuzz" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ast/passes/codegen_llvm.h" #include "ast/passes/field_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpftrace.h" #include "clang_parser.h" #include "driver.h" #include "log.h" #include "output.h" #include "tracepoint_format_parser.h" using namespace bpftrace; int fuzz_main(const char* data, size_t sz); #ifdef LIBFUZZER // main entry for libufuzzer // libfuzzer.a provides main function extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t sz) { fuzz_main((const char*)data, sz); return 0; // Non-zero return values are reserved for future use. } #else // main function for AFL, etc. int main(int argc, char* argv[]) { // Read inputs std::stringstream buf; std::string line; if (argc <= 1) { // Input from stdin (AFL's default) while (std::getline(std::cin, line)) buf << line << std::endl; } else { // Read from file std::string filename(argv[1]); std::ifstream file(filename); if (file.fail()) return 1; buf << file.rdbuf(); } const auto& str = buf.str(); return fuzz_main(str.c_str(), str.size()); } #endif // LIBFUZZER int fuzz_main(const char* data, size_t sz) { if (data == nullptr || sz == 0) return 0; if (getuid() != 0) return 1; DISABLE_LOG(DEBUG); DISABLE_LOG(WARNING); // We can't disable error logs because some functions use a length of error // log to see if an error occurs. Instead, suppress error log output at each // place. // DISABLE_LOG(ERROR); std::ofstream devnull; devnull.open("/dev/null", std::ofstream::out | std::ofstream::app); // reset global states TracepointFormatParser::clear_struct_list(); std::unique_ptr output; std::ostream* os = &std::cout; output = std::make_unique(*os); BPFtrace bpftrace(std::move(output)); bpftrace.safe_mode_ = 0; Driver driver(bpftrace); std::string script(data, sz); driver.source("fuzz", script); // Create AST auto err = driver.parse(); if (err) return err; // Field Analyzer ast::FieldAnalyser fields(driver.ctx, bpftrace, devnull); err = fields.analyse(); if (err) return err; // Tracepoint parser if (TracepointFormatParser::parse(driver.ctx, bpftrace) == false) return 1; // ClangParser ClangParser clang; std::vector extra_flags; { struct utsname utsname; uname(&utsname); std::string ksrc, kobj; auto kdirs = get_kernel_dirs(utsname); ksrc = std::get<0>(kdirs); kobj = std::get<1>(kdirs); if (ksrc != "") extra_flags = get_kernel_cflags( utsname.machine, ksrc, kobj, bpftrace.kconfig); } if (!clang.parse(driver.root.get(), bpftrace, extra_flags)) return 1; err = driver.parse(); if (err) return err; // Semantic Analyzer ast::SemanticAnalyser semantics(driver.ctx, bpftrace, devnull, false); err = semantics.analyse(); if (err) return err; #if defined(TEST_SEMANTIC) return 0; #endif // Create maps err = semantics.create_maps(true); if (err) return err; // Codegen ast::CodegenLLVM llvm(driver.ctx, bpftrace); BpfBytecode bytecode; try { llvm.generate_ir(); llvm.optimize(); bytecode = llvm.emit(false); } catch (const std::system_error& ex) { return 1; } catch (const std::exception& ex) { // failed to compile return 1; } return 0; } bpftrace-0.23.2/src/globalvars.cpp000066400000000000000000000254731477746507000170470ustar00rootroot00000000000000#include "globalvars.h" #include "bpftrace.h" #include "log.h" #include "types.h" #include "utils.h" #include #include #include #include #include #include namespace bpftrace::globalvars { const GlobalVarConfig &get_config(GlobalVar global_var) { auto it = GLOBAL_VAR_CONFIGS.find(global_var); if (it == GLOBAL_VAR_CONFIGS.end()) { LOG(BUG) << "Global variable enum not found in GLOBAL_VAR_CONFIGS"; } return it->second; } static void verify_maps_found( const std::unordered_map §ion_name_to_global_vars_map, const BPFtrace &bpftrace) { for (const auto global_var : bpftrace.resources.needed_global_vars) { auto config = get_config(global_var); if (!section_name_to_global_vars_map.count(config.section)) { LOG(BUG) << "No map found for " << config.section << " which is needed to set global variable " << config.name; } } } static std::unordered_set get_global_vars_for_section( std::string_view target_section, const BPFtrace &bpftrace) { std::unordered_set ret; for (const auto global_var : bpftrace.resources.needed_global_vars) { auto config = get_config(global_var); if (config.section == target_section) { ret.insert(global_var); } } return ret; } static std::map find_btf_var_offsets( const struct bpf_object *bpf_object, std::string_view section_name, const std::unordered_set &needed_global_vars) { struct btf *self_btf = bpf_object__btf(bpf_object); if (!self_btf) { LOG(BUG) << "Failed to get BTF from BPF object"; } __s32 section_id = btf__find_by_name(self_btf, std::string(section_name).c_str()); if (section_id < 0) { LOG(BUG) << "Failed to find section " << section_name << " to update global vars"; } const struct btf_type *section_type = btf__type_by_id( self_btf, static_cast<__u32>(section_id)); if (!section_type) { LOG(BUG) << "Failed to get BTF type for section " << section_name; } // First locate the offsets of each global variable in the section with btf std::map vars_and_offsets; for (const auto global_var : needed_global_vars) { vars_and_offsets[global_var] = -1; } int i; struct btf_var_secinfo *member; for (i = 0, member = btf_var_secinfos(section_type); i < btf_vlen(section_type); ++i, ++member) { const struct btf_type *type_id = btf__type_by_id(self_btf, member->type); if (!type_id) { continue; } std::string_view name = btf__name_by_offset(self_btf, type_id->name_off); // Only deal with bpftrace's known global variables. Other global variables // could come from imported BPF libraries, for example. auto global_var = from_string(name); if (global_var) vars_and_offsets[*global_var] = member->offset; } for (const auto &[global_var, offset] : vars_and_offsets) { if (offset < 0) { LOG(BUG) << "Global variable " << to_string(global_var) << " has not been added to the BPF code " "(codegen_llvm)"; } } return vars_and_offsets; } static void update_global_vars_rodata( const struct bpf_object *bpf_object, std::string_view section_name, struct bpf_map *global_vars_map, const std::unordered_set &needed_global_vars, const BPFtrace &bpftrace) { auto vars_and_offsets = find_btf_var_offsets(bpf_object, section_name, needed_global_vars); size_t v_size; char *global_vars_buf = reinterpret_cast( const_cast(bpf_map__initial_value(global_vars_map, &v_size))); if (!global_vars_buf) { LOG(BUG) << "Failed to get array buf for global variable map"; } // Update the values for the global vars (using the above offsets) for (const auto &[global_var, offset] : vars_and_offsets) { int64_t *var = reinterpret_cast(global_vars_buf + offset); switch (global_var) { case GlobalVar::NUM_CPUS: *var = bpftrace.ncpus_; break; case GlobalVar::MAX_CPU_ID: *var = bpftrace.max_cpu_id_; break; case GlobalVar::FMT_STRINGS_BUFFER: case GlobalVar::TUPLE_BUFFER: case GlobalVar::GET_STR_BUFFER: case GlobalVar::READ_MAP_VALUE_BUFFER: case GlobalVar::WRITE_MAP_VALUE_BUFFER: case GlobalVar::VARIABLE_BUFFER: case GlobalVar::MAP_KEY_BUFFER: break; } } } static void update_global_vars_custom_rw_section( const struct bpf_object *bpf_object, const std::string §ion_name, struct bpf_map *global_vars_map, const std::unordered_set &needed_global_vars, const BPFtrace &bpftrace) { if (needed_global_vars.size() > 1) { LOG(BUG) << "Multiple read-write global variables are in same section " << section_name; } auto global_var = *needed_global_vars.begin(); size_t actual_size; auto buf = bpf_map__initial_value(global_vars_map, &actual_size); if (!buf) { LOG(BUG) << "Failed to get size for section " << section_name << " before resizing"; } if (actual_size == 0) { LOG(BUG) << "Section " << section_name << " has size of 0 "; } auto desired_size = (bpftrace.max_cpu_id_ + 1) * actual_size; auto err = bpf_map__set_value_size(global_vars_map, desired_size); if (err != 0) { throw bpftrace::FatalUserException("Failed to set size to " + std::to_string(desired_size) + " for section " + section_name); } buf = bpf_map__initial_value(global_vars_map, &actual_size); if (!buf) { LOG(BUG) << "Failed to get size for section " << section_name << " after resizing"; } if (actual_size != desired_size) { throw bpftrace::FatalUserException( "Failed to set size from " + std::to_string(actual_size) + " to " + std::to_string(desired_size) + " for section " + section_name); } // No need to memset to zero as we memset on each usage // Verify we can still find variable name via BTF and it hasn't been cleared // after size changes auto vars_and_offset = find_btf_var_offsets(bpf_object, section_name, needed_global_vars); if (vars_and_offset.at(global_var) != 0) { LOG(BUG) << "Read-write global variable " << to_string(global_var) << " must be at offset 0 in section " << section_name; } } void update_global_vars(const struct bpf_object *bpf_object, const std::unordered_map §ion_name_to_global_vars_map, const BPFtrace &bpftrace) { verify_maps_found(section_name_to_global_vars_map, bpftrace); for (const auto &[section_name, global_vars_map] : section_name_to_global_vars_map) { const auto needed_global_variables = get_global_vars_for_section( section_name, bpftrace); if (needed_global_variables.empty()) { continue; } if (section_name == RO_SECTION_NAME) { update_global_vars_rodata(bpf_object, section_name, global_vars_map, needed_global_variables, bpftrace); } else { update_global_vars_custom_rw_section(bpf_object, section_name, global_vars_map, needed_global_variables, bpftrace); } } } std::string to_string(GlobalVar global_var) { return get_config(global_var).name; } std::optional from_string(std::string_view name) { for (const auto &[global_var, config] : GLOBAL_VAR_CONFIGS) { if (config.name == name) return global_var; } return {}; } static SizedType make_rw_type(size_t num_elements, const SizedType &element_type) { auto subtype = CreateArray(num_elements, element_type); // For 1 CPU, will be adjusted to actual CPU count at runtime return CreateArray(1, subtype); } SizedType get_type(bpftrace::globalvars::GlobalVar global_var, const RequiredResources &resources, const Config &bpftrace_config) { switch (global_var) { case bpftrace::globalvars::GlobalVar::NUM_CPUS: case bpftrace::globalvars::GlobalVar::MAX_CPU_ID: return CreateInt64(); case bpftrace::globalvars::GlobalVar::FMT_STRINGS_BUFFER: assert(resources.max_fmtstring_args_size > 0); return make_rw_type( 1, CreateArray(resources.max_fmtstring_args_size, CreateInt8())); case bpftrace::globalvars::GlobalVar::TUPLE_BUFFER: assert(resources.max_tuple_size > 0); assert(resources.tuple_buffers > 0); return make_rw_type(resources.tuple_buffers, CreateArray(resources.max_tuple_size, CreateInt8())); case bpftrace::globalvars::GlobalVar::GET_STR_BUFFER: { assert(resources.str_buffers > 0); const auto max_strlen = bpftrace_config.get(ConfigKeyInt::max_strlen); return make_rw_type(resources.str_buffers, CreateArray(max_strlen, CreateInt8())); } case bpftrace::globalvars::GlobalVar::READ_MAP_VALUE_BUFFER: assert(resources.max_read_map_value_size > 0); assert(resources.read_map_value_buffers > 0); return make_rw_type(resources.read_map_value_buffers, CreateArray(resources.max_read_map_value_size, CreateInt8())); case bpftrace::globalvars::GlobalVar::WRITE_MAP_VALUE_BUFFER: assert(resources.max_write_map_value_size > 0); return make_rw_type( 1, CreateArray(resources.max_write_map_value_size, CreateInt8())); case GlobalVar::VARIABLE_BUFFER: assert(resources.variable_buffers > 0); assert(resources.max_variable_size > 0); return make_rw_type(resources.variable_buffers, CreateArray(resources.max_variable_size, CreateInt8())); case GlobalVar::MAP_KEY_BUFFER: assert(resources.map_key_buffers > 0); assert(resources.max_map_key_size > 0); return make_rw_type(resources.map_key_buffers, CreateArray(resources.max_map_key_size, CreateInt8())); } return {}; // unreachable } std::unordered_set get_section_names() { std::unordered_set ret; for (const auto &[_, config] : GLOBAL_VAR_CONFIGS) { ret.insert(config.section); } return ret; } } // namespace bpftrace::globalvars bpftrace-0.23.2/src/globalvars.h000066400000000000000000000047741477746507000165150ustar00rootroot00000000000000#pragma once #include #include #include #include "bpftrace.h" #include "types.h" #include #include namespace bpftrace { namespace globalvars { std::string to_string(GlobalVar global_var); std::optional from_string(std::string_view name); constexpr std::string_view RO_SECTION_NAME = ".rodata"; constexpr std::string_view FMT_STRINGS_BUFFER_SECTION_NAME = ".data.fmt_str_buf"; constexpr std::string_view TUPLE_BUFFER_SECTION_NAME = ".data.tuple_buf"; constexpr std::string_view GET_STR_BUFFER_SECTION_NAME = ".data.get_str_buf"; constexpr std::string_view READ_MAP_VALUE_BUFFER_SECTION_NAME = ".data.read_map_val_buf"; constexpr std::string_view WRITE_MAP_VALUE_BUFFER_SECTION_NAME = ".data.write_map_val_buf"; constexpr std::string_view VARIABLE_BUFFER_SECTION_NAME = ".data.var_buf"; constexpr std::string_view MAP_KEY_BUFFER_SECTION_NAME = ".data.map_key_buf"; struct GlobalVarConfig { std::string name; std::string section; bool read_only; }; const std::unordered_map GLOBAL_VAR_CONFIGS = { { GlobalVar::NUM_CPUS, { "num_cpus", std::string(RO_SECTION_NAME), true } }, { GlobalVar::MAX_CPU_ID, { "max_cpu_id", std::string(RO_SECTION_NAME), true } }, { GlobalVar::FMT_STRINGS_BUFFER, { "fmt_str_buf", std::string(FMT_STRINGS_BUFFER_SECTION_NAME), false } }, { GlobalVar::TUPLE_BUFFER, { "tuple_buf", std::string(TUPLE_BUFFER_SECTION_NAME), false } }, { GlobalVar::GET_STR_BUFFER, { "get_str_buf", std::string(GET_STR_BUFFER_SECTION_NAME), false } }, { GlobalVar::READ_MAP_VALUE_BUFFER, { "read_map_val_buf", std::string(READ_MAP_VALUE_BUFFER_SECTION_NAME), false } }, { GlobalVar::WRITE_MAP_VALUE_BUFFER, { "write_map_val_buf", std::string(WRITE_MAP_VALUE_BUFFER_SECTION_NAME), false } }, { GlobalVar::VARIABLE_BUFFER, { "var_buf", std::string(VARIABLE_BUFFER_SECTION_NAME), false } }, { GlobalVar::MAP_KEY_BUFFER, { "map_key_buf", std::string(MAP_KEY_BUFFER_SECTION_NAME), false } }, }; void update_global_vars( const struct bpf_object *obj, const std::unordered_map &global_vars_map, const BPFtrace &bpftrace); const GlobalVarConfig &get_config(GlobalVar global_var); SizedType get_type(GlobalVar global_var, const RequiredResources &resources, const Config &bpftrace_config); std::unordered_set get_section_names(); } // namespace globalvars } // namespace bpftrace bpftrace-0.23.2/src/kfuncs.h000066400000000000000000000005561477746507000156440ustar00rootroot00000000000000#pragma once #include #include namespace bpftrace { enum class Kfunc { bpf_map_sum_elem_count, }; static const std::map KFUNC_NAME_MAP = { { Kfunc::bpf_map_sum_elem_count, "bpf_map_sum_elem_count" }, }; inline const std::string &kfunc_name(enum Kfunc kfunc) { return KFUNC_NAME_MAP.at(kfunc); } } // namespace bpftrace bpftrace-0.23.2/src/ksyms.cpp000066400000000000000000000051701477746507000160510ustar00rootroot00000000000000#include #include #ifdef HAVE_BLAZESYM #include #endif #include "config.h" #include "ksyms.h" #include "scopeguard.h" #include "utils.h" namespace { std::string stringify_addr(uint64_t addr) { std::ostringstream symbol; symbol << reinterpret_cast(addr); return symbol.str(); } } // namespace namespace bpftrace { Ksyms::Ksyms(const Config &config) : config_(config) { } Ksyms::~Ksyms() { if (ksyms_) bcc_free_symcache(ksyms_, -1); #ifdef HAVE_BLAZESYM if (symbolizer_) blaze_symbolizer_free(symbolizer_); #endif } std::string Ksyms::resolve_bcc(uint64_t addr, bool show_offset) { struct bcc_symbol ksym; if (!ksyms_) ksyms_ = bcc_symcache_new(-1, nullptr); if (bcc_symcache_resolve(ksyms_, addr, &ksym) == 0) { std::ostringstream symbol; symbol << ksym.name; if (show_offset) symbol << "+" << ksym.offset; return symbol.str(); } return stringify_addr(addr); } #ifdef HAVE_BLAZESYM std::optional Ksyms::resolve_blazesym_int(uint64_t addr, bool show_offset) { if (symbolizer_ == nullptr) { symbolizer_ = blaze_symbolizer_new(); if (symbolizer_ == nullptr) return std::nullopt; } std::ostringstream symbol; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" blaze_symbolize_src_kernel src = { .type_size = sizeof(src), // Use default system-wide kallsyms file. .kallsyms = nullptr, // Disable discovery and usage of a vmlinux file. // TODO: We should eventually support that, incorporating discovery logic // from find_vmlinux(). .vmlinux = "", }; #pragma GCC diagnostic pop uint64_t addrs[1] = { addr }; const blaze_syms *syms = blaze_symbolize_kernel_abs_addrs( symbolizer_, &src, addrs, ARRAY_SIZE(addrs)); if (syms == nullptr) return std::nullopt; SCOPE_EXIT { blaze_syms_free(syms); }; const blaze_sym *sym = &syms->syms[0]; if (sym->name == nullptr) { return std::nullopt; } symbol << sym->name; if (show_offset) { auto offset = addr - sym->addr; symbol << "+" << offset; } return symbol.str(); } std::string Ksyms::resolve_blazesym(uint64_t addr, bool show_offset) { if (auto sym = resolve_blazesym_int(addr, show_offset)) { return *sym; } return stringify_addr(addr); } #endif std::string Ksyms::resolve(uint64_t addr, bool show_offset) { #ifdef HAVE_BLAZESYM if (config_.get(ConfigKeyBool::use_blazesym)) return resolve_blazesym(addr, show_offset); #endif return resolve_bcc(addr, show_offset); } } // namespace bpftrace bpftrace-0.23.2/src/ksyms.h000066400000000000000000000013101477746507000155060ustar00rootroot00000000000000#pragma once #include #include namespace bpftrace { class Config; class Ksyms { public: Ksyms(const Config &config); ~Ksyms(); Ksyms(Ksyms &) = delete; Ksyms &operator=(const Ksyms &) = delete; std::string resolve(uint64_t addr, bool show_offset); private: const Config &config_; void *ksyms_{ nullptr }; #ifdef HAVE_BLAZESYM struct blaze_symbolizer *symbolizer_{ nullptr }; std::optional resolve_blazesym_int(uint64_t addr, bool show_offset); std::string resolve_blazesym(uint64_t addr, bool show_offset); #endif std::string resolve_bcc(uint64_t addr, bool show_offset); }; } // namespace bpftrace bpftrace-0.23.2/src/lexer.h000066400000000000000000000004411477746507000154630ustar00rootroot00000000000000#pragma once #include "driver.h" #include "parser.tab.hh" #define YY_DECL \ bpftrace::Parser::symbol_type yylex(bpftrace::Driver &driver, \ yyscan_t yyscanner) YY_DECL; bpftrace-0.23.2/src/lexer.l000066400000000000000000000331321477746507000154720ustar00rootroot00000000000000%option yylineno noyywrap noinput %option never-interactive %option reentrant %option stack %{ #include #include "driver.h" #include "utils.h" #include "parser.tab.hh" #include "ast/int_parser.h" #include "lexer.h" bpftrace::location loc; static std::string struct_type; static std::string buffer; #define YY_USER_ACTION loc.columns(yyleng); #define yyterminate() return bpftrace::Parser::make_END(loc) using namespace bpftrace; %} /* https://en.cppreference.com/w/cpp/language/integer_literal#The_type_of_the_literal */ int_size (([uU])|([uU]?[lL]?[lL])) /* Number with underscores in it, e.g. 1_000_000 */ int [0-9]([0-9_]*[0-9])?{int_size}? hex 0[xX][0-9a-fA-F]+ /* scientific notation, e.g. 2e4 or 1e6 */ exponent {int}[eE]{int} ident [_a-zA-Z][_a-zA-Z0-9]* map @{ident}|@ var ${ident} hspace [ \t] vspace [\n\r] space {hspace}|{vspace} path :(\\.|[_\-\./a-zA-Z0-9#+\*])+ builtin arg[0-9]|args|cgroup|comm|cpid|numaid|cpu|ctx|curtask|elapsed|func|gid|pid|probe|rand|retval|sarg[0-9]|tid|uid|username|jiffies call avg|buf|cat|cgroupid|clear|count|delete|exit|hist|join|kaddr|kptr|ksym|len|lhist|macaddr|max|min|ntop|override|print|printf|cgroup_path|reg|signal|stats|str|strerror|strftime|strncmp|strcontains|sum|system|time|uaddr|uptr|usym|zero|path|unwatch|bswap|skboutput|pton|debugf|has_key|percpu_kaddr int_type bool|(u)?int(8|16|32|64) builtin_type void|(u)?(min|max|sum|count|avg|stats)_t|probe_t|username|lhist_t|hist_t|usym_t|ksym_t|timestamp|macaddr_t|cgroup_path_t|strerror_t|kstack_t|ustack_t sized_type string|inet|buffer subprog fn /* Don't add to this! Use builtin OR call not both */ call_and_builtin kstack|ustack|nsecs /* escape sequences in strings */ hex_esc (x|X)[0-9a-fA-F]{1,2} oct_esc [0-7]{1,3} %x STR %x STRUCT %x ENUM %x BRACE %x COMMENT %x AFTER_COLON %x STRUCT_AFTER_COLON %% {hspace}+ { loc.step(); } {vspace}+ { loc.lines(yyleng); loc.step(); } ^"#!".*$ // executable line "//".*$ // single-line comments "/*" yy_push_state(COMMENT, yyscanner); { "*/" yy_pop_state(yyscanner); [^*\n]+|"*" {} \n loc.lines(1); loc.step(); <> yy_pop_state(yyscanner); driver.error(loc, "end of file during comment"); } bpftrace|perf|raw { return Parser::make_STACK_MODE(yytext, loc); } {builtin} { return Parser::make_BUILTIN(yytext, loc); } {call} { return Parser::make_CALL(yytext, loc); } {call_and_builtin} { return Parser::make_CALL_BUILTIN(yytext, loc); } {subprog} { return Parser::make_SUBPROG(yytext, loc); } {int}|{hex}|{exponent} { try { auto res = ast::int_parser::to_uint(yytext, 0); return Parser::make_INT(res, loc); } catch (const std::exception &e) { driver.error(loc, e.what()); } } {path} { return Parser::make_PATH(yytext, loc); } {map} { return Parser::make_MAP(yytext, loc); } {var} { return Parser::make_VAR(yytext, loc); } ":" { /* For handling "struct x" in "fn name(...): struct x { }" as a type rather than a beginning of a struct definition; see AFTER_COLON rules below */ yy_push_state(AFTER_COLON, yyscanner); return Parser::make_COLON(loc); } ";" { return Parser::make_SEMI(loc); } "{" { return Parser::make_LBRACE(loc); } "}" { return Parser::make_RBRACE(loc); } "[" { return Parser::make_LBRACKET(loc); } "]" { return Parser::make_RBRACKET(loc); } "(" { return Parser::make_LPAREN(loc); } ")" { return Parser::make_RPAREN(loc); } \//{space}*[\/\{] { return Parser::make_ENDPRED(loc); } /* If "/" is followed by "/" or "{", choose ENDPRED, otherwise DIV */ "," { return Parser::make_COMMA(loc); } "=" { return Parser::make_ASSIGN(loc); } "<<=" { return Parser::make_LEFTASSIGN(loc); } ">>=" { return Parser::make_RIGHTASSIGN(loc); } "+=" { return Parser::make_PLUSASSIGN(loc); } "-=" { return Parser::make_MINUSASSIGN(loc); } "*=" { return Parser::make_MULASSIGN(loc); } "/=" { return Parser::make_DIVASSIGN(loc); } "%=" { return Parser::make_MODASSIGN(loc); } "&=" { return Parser::make_BANDASSIGN(loc); } "|=" { return Parser::make_BORASSIGN(loc); } "^=" { return Parser::make_BXORASSIGN(loc); } "==" { return Parser::make_EQ(loc); } "!=" { return Parser::make_NE(loc); } "<=" { return Parser::make_LE(loc); } ">=" { return Parser::make_GE(loc); } "<<" { return Parser::make_LEFT(loc); } ">>" { return Parser::make_RIGHT(loc); } "<" { return Parser::make_LT(loc); } ">" { return Parser::make_GT(loc); } "&&" { return Parser::make_LAND(loc); } "||" { return Parser::make_LOR(loc); } "+" { return Parser::make_PLUS(loc); } "-" { return Parser::make_MINUS(loc); } "++" { return Parser::make_INCREMENT(loc); } "--" { return Parser::make_DECREMENT(loc); } "*" { return Parser::make_MUL(loc); } "/" { return Parser::make_DIV(loc); } "%" { return Parser::make_MOD(loc); } "&" { return Parser::make_BAND(loc); } "|" { return Parser::make_BOR(loc); } "^" { return Parser::make_BXOR(loc); } "!" { return Parser::make_LNOT(loc); } "~" { return Parser::make_BNOT(loc); } "." { return Parser::make_DOT(loc); } "->" { return Parser::make_PTR(loc); } "$"[0-9]+ { return Parser::make_PARAM(yytext, loc); } "$"# { return Parser::make_PARAMCOUNT(loc); } "#"[^!].* { return Parser::make_CPREPROC(yytext, loc); } "if" { return Parser::make_IF(yytext, loc); } "else" { return Parser::make_ELSE(yytext, loc); } "?" { return Parser::make_QUES(loc); } "unroll" { return Parser::make_UNROLL(yytext, loc); } "while" { return Parser::make_WHILE(yytext, loc); } "config" { return Parser::make_CONFIG(yytext, loc); } "for" { return Parser::make_FOR(yytext, loc); } "return" { return Parser::make_RETURN(yytext, loc); } "continue" { return Parser::make_CONTINUE(yytext, loc); } "break" { return Parser::make_BREAK(yytext, loc); } "sizeof" { return Parser::make_SIZEOF(yytext, loc); } "offsetof" { return Parser::make_OFFSETOF(yytext, loc); } "let" { return Parser::make_LET(yytext, loc); } {int_type} { return Parser::make_INT_TYPE(yytext, loc); } {builtin_type} { return Parser::make_BUILTIN_TYPE(yytext, loc); } {sized_type} { return Parser::make_SIZED_TYPE(yytext, loc); } \" { yy_push_state(STR, yyscanner); buffer.clear(); } { \" { yy_pop_state(yyscanner); return Parser::make_STRING(buffer, loc); } [^\\\n\"]+ buffer += yytext; \\n buffer += '\n'; \\t buffer += '\t'; \\r buffer += '\r'; \\\" buffer += '\"'; \\\\ buffer += '\\'; \\{oct_esc} { long value = strtol(yytext+1, NULL, 8); if (value > UCHAR_MAX) driver.error(loc, std::string("octal escape sequence out of range '") + yytext + "'"); buffer += value; } \\{hex_esc} buffer += strtol(yytext+2, NULL, 16); \n driver.error(loc, "unterminated string"); yy_pop_state(yyscanner); loc.lines(1); loc.step(); <> driver.error(loc, "unterminated string"); yy_pop_state(yyscanner); \\. { driver.error(loc, std::string("invalid escape character '") + yytext + "'"); } . driver.error(loc, "invalid character"); yy_pop_state(yyscanner); } struct|union|enum { yy_push_state(STRUCT, yyscanner); buffer.clear(); struct_type = yytext; return Parser::make_STRUCT(loc); } { {hspace}+ { loc.step(); } {vspace}+ { loc.lines(yyleng); loc.step(); } struct|union|enum { yy_pop_state(yyscanner); yy_push_state(STRUCT_AFTER_COLON, yyscanner); buffer.clear(); struct_type = yytext; return Parser::make_STRUCT(loc); } . { unput(yytext[0]); yy_pop_state(yyscanner); } } { {hspace}+ { loc.step(); } {vspace}+ { loc.lines(yyleng); loc.step(); } {ident} { buffer = yytext; yy_pop_state(yyscanner); return Parser::make_IDENT(struct_type + " " + trim(buffer), loc); } } { "*"|")"|"," { if (YY_START == STRUCT) { // Finished parsing the typename of a cast or a call arg // Put the cast type into a canonical form by trimming // and then inserting a single space. yy_pop_state(yyscanner); for (int i = yyleng - 1; i >= 0; i--) unput(yytext[i]); return Parser::make_IDENT(struct_type + " " + trim(buffer), loc); } buffer += yytext[0]; } "{" yy_push_state(BRACE, yyscanner); buffer += '{'; "}"|"};" { buffer += yytext; yy_pop_state(yyscanner); if (YY_START == STRUCT) { // Finished parsing a struct definition // Trimming isn't needed here since the typenames // will go through Clang before we get them back // anyway. yy_pop_state(yyscanner); return Parser::make_STRUCT_DEFN(struct_type + buffer, loc); } } . buffer += yytext[0]; \n buffer += '\n'; loc.lines(1); loc.step(); } {ident} { static int unput_count = 0; if (driver.bpftrace_.macros_.count(yytext) != 0) { const char *s = driver.bpftrace_.macros_[yytext].c_str(); int z; // NOTE(mmarchini) workaround for simple recursive // macros. More complex recursive macros (for // example, with operators) will go into an // infinite loop. Yes, we should fix that in the // future. if (strcmp(s, yytext) == 0) { unput_count = 0; return Parser::make_IDENT(yytext, loc); } else { int leng = YY_BUF_SIZE; std::string original_s(s); std::string original_yytext(yytext); for (z=strlen(s) - 1; z >= 0; z--){ if (unput_count >= 1000 || leng <= 0) { driver.error(loc, std::string("Macro recursion limit reached: ") + original_yytext + ", " + original_s); yyterminate(); } unput(s[z]); unput_count++; leng--; } } } else { unput_count = 0; return Parser::make_IDENT(yytext, loc); } } . { driver.error(loc, std::string("invalid character '") + std::string(yytext) + std::string("'")); } %% bpftrace-0.23.2/src/libbpf/000077500000000000000000000000001477746507000154325ustar00rootroot00000000000000bpftrace-0.23.2/src/libbpf/bpf.h000066400000000000000000000220421477746507000163520ustar00rootroot00000000000000#pragma once #ifndef BPF_PSEUDO_MAP_FD #define BPF_PSEUDO_MAP_FD 1 #endif #ifndef BPF_PSEUDO_MAP_VALUE #define BPF_PSEUDO_MAP_VALUE 2 #endif #ifndef BPF_F_KPROBE_MULTI_RETURN #define BPF_F_KPROBE_MULTI_RETURN (1U << 0) #endif #ifndef BPF_F_UPROBE_MULTI_RETURN #define BPF_F_UPROBE_MULTI_RETURN (1U << 0) #endif // clang-format off enum bpf_map_type { BPF_MAP_TYPE_UNSPEC, BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PROG_ARRAY, BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_MAP_TYPE_PERCPU_HASH, BPF_MAP_TYPE_PERCPU_ARRAY, BPF_MAP_TYPE_STACK_TRACE, BPF_MAP_TYPE_CGROUP_ARRAY, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_LPM_TRIE, BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_DEVMAP, BPF_MAP_TYPE_SOCKMAP, BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, BPF_MAP_TYPE_CGROUP_STORAGE, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, BPF_MAP_TYPE_QUEUE, BPF_MAP_TYPE_STACK, BPF_MAP_TYPE_SK_STORAGE, BPF_MAP_TYPE_DEVMAP_HASH, BPF_MAP_TYPE_STRUCT_OPS, BPF_MAP_TYPE_RINGBUF, BPF_MAP_TYPE_INODE_STORAGE, BPF_MAP_TYPE_TASK_STORAGE, BPF_MAP_TYPE_BLOOM_FILTER, }; enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC, BPF_PROG_TYPE_SOCKET_FILTER, BPF_PROG_TYPE_KPROBE, BPF_PROG_TYPE_SCHED_CLS, BPF_PROG_TYPE_SCHED_ACT, BPF_PROG_TYPE_TRACEPOINT, BPF_PROG_TYPE_XDP, BPF_PROG_TYPE_PERF_EVENT, BPF_PROG_TYPE_CGROUP_SKB, BPF_PROG_TYPE_CGROUP_SOCK, BPF_PROG_TYPE_LWT_IN, BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, BPF_PROG_TYPE_CGROUP_DEVICE, BPF_PROG_TYPE_SK_MSG, BPF_PROG_TYPE_RAW_TRACEPOINT, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_PROG_TYPE_LWT_SEG6LOCAL, BPF_PROG_TYPE_LIRC_MODE2, BPF_PROG_TYPE_SK_REUSEPORT, BPF_PROG_TYPE_FLOW_DISSECTOR, BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_PROG_TYPE_TRACING, BPF_PROG_TYPE_STRUCT_OPS, BPF_PROG_TYPE_EXT, BPF_PROG_TYPE_LSM, BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SYSCALL, }; enum bpf_attach_type { BPF_CGROUP_INET_INGRESS, BPF_CGROUP_INET_EGRESS, BPF_CGROUP_INET_SOCK_CREATE, BPF_CGROUP_SOCK_OPS, BPF_SK_SKB_STREAM_PARSER, BPF_SK_SKB_STREAM_VERDICT, BPF_CGROUP_DEVICE, BPF_SK_MSG_VERDICT, BPF_CGROUP_INET4_BIND, BPF_CGROUP_INET6_BIND, BPF_CGROUP_INET4_CONNECT, BPF_CGROUP_INET6_CONNECT, BPF_CGROUP_INET4_POST_BIND, BPF_CGROUP_INET6_POST_BIND, BPF_CGROUP_UDP4_SENDMSG, BPF_CGROUP_UDP6_SENDMSG, BPF_LIRC_MODE2, BPF_FLOW_DISSECTOR, BPF_CGROUP_SYSCTL, BPF_CGROUP_UDP4_RECVMSG, BPF_CGROUP_UDP6_RECVMSG, BPF_CGROUP_GETSOCKOPT, BPF_CGROUP_SETSOCKOPT, BPF_TRACE_RAW_TP, BPF_TRACE_FENTRY, BPF_TRACE_FEXIT, BPF_MODIFY_RETURN, BPF_LSM_MAC, BPF_TRACE_ITER, BPF_CGROUP_INET4_GETPEERNAME, BPF_CGROUP_INET6_GETPEERNAME, BPF_CGROUP_INET4_GETSOCKNAME, BPF_CGROUP_INET6_GETSOCKNAME, BPF_XDP_DEVMAP, BPF_CGROUP_INET_SOCK_RELEASE, BPF_XDP_CPUMAP, BPF_SK_LOOKUP, BPF_XDP, BPF_SK_SKB_VERDICT, BPF_SK_REUSEPORT_SELECT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, BPF_PERF_EVENT, BPF_TRACE_KPROBE_MULTI, BPF_LSM_CGROUP, BPF_STRUCT_OPS, BPF_NETFILTER, BPF_TCX_INGRESS, BPF_TCX_EGRESS, BPF_TRACE_UPROBE_MULTI, }; #ifdef __BPF_FUNC_MAPPER #undef __BPF_FUNC_MAPPER #endif #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ FN(map_lookup_elem), \ FN(map_update_elem), \ FN(map_delete_elem), \ FN(probe_read), \ FN(ktime_get_ns), \ FN(trace_printk), \ FN(get_prandom_u32), \ FN(get_smp_processor_id), \ FN(skb_store_bytes), \ FN(l3_csum_replace), \ FN(l4_csum_replace), \ FN(tail_call), \ FN(clone_redirect), \ FN(get_current_pid_tgid), \ FN(get_current_uid_gid), \ FN(get_current_comm), \ FN(get_cgroup_classid), \ FN(skb_vlan_push), \ FN(skb_vlan_pop), \ FN(skb_get_tunnel_key), \ FN(skb_set_tunnel_key), \ FN(perf_event_read), \ FN(redirect), \ FN(get_route_realm), \ FN(perf_event_output), \ FN(skb_load_bytes), \ FN(get_stackid), \ FN(csum_diff), \ FN(skb_get_tunnel_opt), \ FN(skb_set_tunnel_opt), \ FN(skb_change_proto), \ FN(skb_change_type), \ FN(skb_under_cgroup), \ FN(get_hash_recalc), \ FN(get_current_task), \ FN(probe_write_user), \ FN(current_task_under_cgroup), \ FN(skb_change_tail), \ FN(skb_pull_data), \ FN(csum_update), \ FN(set_hash_invalid), \ FN(get_numa_node_id), \ FN(skb_change_head), \ FN(xdp_adjust_head), \ FN(probe_read_str), \ FN(get_socket_cookie), \ FN(get_socket_uid), \ FN(set_hash), \ FN(setsockopt), \ FN(skb_adjust_room), \ FN(redirect_map), \ FN(sk_redirect_map), \ FN(sock_map_update), \ FN(xdp_adjust_meta), \ FN(perf_event_read_value), \ FN(perf_prog_read_value), \ FN(getsockopt), \ FN(override_return), \ FN(sock_ops_cb_flags_set), \ FN(msg_redirect_map), \ FN(msg_apply_bytes), \ FN(msg_cork_bytes), \ FN(msg_pull_data), \ FN(bind), \ FN(xdp_adjust_tail), \ FN(skb_get_xfrm_state), \ FN(get_stack), \ FN(skb_load_bytes_relative), \ FN(fib_lookup), \ FN(sock_hash_update), \ FN(msg_redirect_hash), \ FN(sk_redirect_hash), \ FN(lwt_push_encap), \ FN(lwt_seg6_store_bytes), \ FN(lwt_seg6_adjust_srh), \ FN(lwt_seg6_action), \ FN(rc_repeat), \ FN(rc_keydown), \ FN(skb_cgroup_id), \ FN(get_current_cgroup_id), \ FN(get_local_storage), \ FN(sk_select_reuseport), \ FN(skb_ancestor_cgroup_id), \ FN(sk_lookup_tcp), \ FN(sk_lookup_udp), \ FN(sk_release), \ FN(map_push_elem), \ FN(map_pop_elem), \ FN(map_peek_elem), \ FN(msg_push_data), \ FN(msg_pop_data), \ FN(rc_pointer_rel), \ FN(spin_lock), \ FN(spin_unlock), \ FN(sk_fullsock), \ FN(tcp_sock), \ FN(skb_ecn_set_ce), \ FN(get_listener_sock), \ FN(skc_lookup_tcp), \ FN(tcp_check_syncookie), \ FN(sysctl_get_name), \ FN(sysctl_get_current_value), \ FN(sysctl_get_new_value), \ FN(sysctl_set_new_value), \ FN(strtol), \ FN(strtoul), \ FN(sk_storage_get), \ FN(sk_storage_delete), \ FN(send_signal), \ FN(tcp_gen_syncookie), \ FN(skb_output), \ FN(probe_read_user), \ FN(probe_read_kernel), \ FN(probe_read_user_str), \ FN(probe_read_kernel_str), \ FN(tcp_send_ack), \ FN(send_signal_thread), \ FN(jiffies64), \ FN(read_branch_records), \ FN(get_ns_current_pid_tgid), \ FN(xdp_output), \ FN(get_netns_cookie), \ FN(get_current_ancestor_cgroup_id), \ FN(sk_assign), \ FN(ktime_get_boot_ns), \ FN(seq_printf), \ FN(seq_write), \ FN(sk_cgroup_id), \ FN(sk_ancestor_cgroup_id), \ FN(ringbuf_output), \ FN(ringbuf_reserve), \ FN(ringbuf_submit), \ FN(ringbuf_discard), \ FN(ringbuf_query), \ FN(csum_level), \ FN(skc_to_tcp6_sock), \ FN(skc_to_tcp_sock), \ FN(skc_to_tcp_timewait_sock), \ FN(skc_to_tcp_request_sock), \ FN(skc_to_udp6_sock), \ FN(get_task_stack), \ FN(load_hdr_opt), \ FN(store_hdr_opt), \ FN(reserve_hdr_opt), \ FN(inode_storage_get), \ FN(inode_storage_delete), \ FN(d_path), \ FN(copy_from_user), \ FN(snprintf_btf), \ FN(seq_printf_btf), \ FN(skb_cgroup_classid), \ FN(redirect_neigh), \ FN(per_cpu_ptr), \ FN(this_cpu_ptr), \ FN(redirect_peer), \ FN(task_storage_get), \ FN(task_storage_delete), \ FN(get_current_task_btf), \ FN(bprm_opts_set), \ FN(ktime_get_coarse_ns), \ FN(ima_inode_hash), \ FN(sock_from_file), \ FN(check_mtu), \ FN(for_each_map_elem), \ FN(snprintf), \ FN(sys_bpf), \ FN(btf_find_by_name_kind), \ FN(sys_close), \ FN(timer_init), \ FN(timer_set_callback), \ FN(timer_start), \ FN(timer_cancel), \ FN(get_func_ip), \ FN(get_attach_cookie), \ FN(task_pt_regs), \ FN(get_branch_snapshot), \ FN(trace_vprintk), \ FN(skc_to_unix_sock), \ FN(kallsyms_lookup_name), \ FN(find_vma), \ FN(loop), \ FN(strncmp), \ FN(get_func_arg), \ FN(get_func_ret), \ FN(get_func_arg_cnt), \ FN(get_retval), \ FN(set_retval), \ FN(xdp_get_buff_len), \ FN(xdp_load_bytes), \ FN(xdp_store_bytes), \ FN(copy_from_user_task), \ FN(skb_set_tstamp), \ FN(ima_file_hash), \ FN(kptr_xchg), \ FN(map_lookup_percpu_elem), \ FN(skc_to_mptcp_sock), \ FN(dynptr_from_mem), \ FN(ringbuf_reserve_dynptr), \ FN(ringbuf_submit_dynptr), \ FN(ringbuf_discard_dynptr), \ FN(dynptr_read), \ FN(dynptr_write), \ FN(dynptr_data), \ FN(tcp_raw_gen_syncookie_ipv4), \ FN(tcp_raw_gen_syncookie_ipv6), \ FN(tcp_raw_check_syncookie_ipv4), \ FN(tcp_raw_check_syncookie_ipv6), \ FN(ktime_get_tai_ns), \ FN(user_ringbuf_drain), \ FN(cgrp_storage_get), \ FN(cgrp_storage_delete), // integer value in 'imm' field of BPF_CALL instruction selects which helper // function eBPF program intends to call #define __BPF_ENUM_FN(x) BPF_FUNC_ ## x enum bpf_func_id { __BPF_FUNC_MAPPER(__BPF_ENUM_FN) __BPF_FUNC_MAX_ID, }; #undef __BPF_ENUM_FN #define BPFTRACE_LIBBPF_OPTS(TYPE, NAME, ...) \ _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") \ LIBBPF_OPTS(TYPE, NAME, __VA_ARGS__) // clang-format on bpftrace-0.23.2/src/lockdown.cpp000066400000000000000000000030741477746507000165240ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "lockdown.h" namespace bpftrace::lockdown { static LockdownState from_string(const std::string &s) { if (s == "none") return LockdownState::None; else if (s == "integrity") return LockdownState::Integrity; else if (s == "confidentiality") return LockdownState::Confidentiality; return LockdownState::Unknown; } static LockdownState read_security_lockdown() { std::ifstream file("/sys/kernel/security/lockdown"); if (file.fail()) return LockdownState::Unknown; // Format: none [integrity] confidentiality // read one field at a time, if it starts with [ it's the one we want while (!file.fail()) { std::string field; file >> field; if (field[0] == '[') return from_string(field.substr(1, field.length() - 2)); } return LockdownState::Unknown; } void emit_warning(std::ostream &out) { // clang-format off // these lines are ~80 chars wide in terminal out << "Kernel lockdown is enabled and set to 'confidentiality'. Lockdown mode blocks" << std::endl << "parts of BPF which makes it impossible for bpftrace to function. Please see " << std::endl << "https://github.com/bpftrace/bpftrace/blob/master/INSTALL.md#disable-lockdown" << std::endl << "for more details on lockdown and how to disable it." << std::endl; // clang-format on } LockdownState detect() { return read_security_lockdown(); } } // namespace bpftrace::lockdown bpftrace-0.23.2/src/lockdown.h000066400000000000000000000005441477746507000161700ustar00rootroot00000000000000#pragma once #include #include "bpffeature.h" namespace bpftrace { namespace lockdown { enum class LockdownState { None, Integrity, Confidentiality, Unknown, // Could not determine whether lockdown is enabled or not }; LockdownState detect(); void emit_warning(std::ostream &out); } // namespace lockdown } // namespace bpftrace bpftrace-0.23.2/src/log.cpp000066400000000000000000000142441477746507000154660ustar00rootroot00000000000000#include "log.h" namespace bpftrace { std::string logtype_str(LogType t) { switch (t) { // clang-format off case LogType::DEBUG : return ""; case LogType::V1 : return ""; case LogType::HINT : return "HINT: "; case LogType::WARNING : return "WARNING: "; case LogType::ERROR : return "ERROR: "; case LogType::BUG : return "BUG: "; // clang-format on } return {}; // unreached } Log::Log() { enabled_map_[LogType::DEBUG] = true; enabled_map_[LogType::V1] = false; enabled_map_[LogType::HINT] = true; enabled_map_[LogType::WARNING] = true; enabled_map_[LogType::ERROR] = true; enabled_map_[LogType::BUG] = true; } Log& Log::get() { static Log log; return log; } void Log::take_input(LogType type, const std::optional& loc, std::ostream& out, std::string&& input) { if (loc) { if (src_.empty()) { std::cerr << "Log: cannot resolve location before calling set_source()." << std::endl; out << log_format_output(type, std::move(input)); } else if (loc->begin.line == 0) { std::cerr << "Log: invalid location." << std::endl; out << log_format_output(type, std::move(input)); } else if (loc->begin.line > loc->end.line) { std::cerr << "Log: loc.begin > loc.end: " << loc->begin << ":" << loc->end << std::endl; out << log_format_output(type, std::move(input)); } else { log_with_location(type, loc.value(), out, input); } } else { out << log_format_output(type, std::move(input)); } } std::string Log::log_format_output(LogType type, std::string&& input) { if (!is_colorize_) { return logtype_str(type) + std::move(input) + '\n'; } std::string color; switch (type) { case LogType::ERROR: color = LogColor::RED; break; case LogType::WARNING: color = LogColor::YELLOW; break; default: return logtype_str(type) + std::move(input) + '\n'; } return color + logtype_str(type) + std::move(input) + LogColor::RESET + '\n'; } const std::string Log::get_source_line(unsigned int n) { // Get the Nth source line (N is 0-based). Return an empty string if it // doesn't exist std::string line; std::stringstream ss(src_); for (unsigned int idx = 0; idx <= n; idx++) { std::getline(ss, line); if (ss.eof() && idx == n) return line; if (!ss) return ""; } return line; } void Log::log_with_location(LogType type, const location& l, std::ostream& out, const std::string& m) { const char* color_begin = LogColor::DEFAULT; const char* color_end = LogColor::DEFAULT; if (is_colorize_) { switch (type) { case LogType::ERROR: color_begin = LogColor::RED; color_end = LogColor::RESET; break; case LogType::WARNING: color_begin = LogColor::YELLOW; color_end = LogColor::RESET; break; default: break; } } out << color_begin; if (filename_.size()) { out << filename_ << ":"; } std::string msg(m); const std::string& typestr = logtype_str(type); if (!msg.empty() && msg.back() == '\n') { msg.pop_back(); } // For a multi line error only the line range is printed: // :-: ERROR: if (l.begin.line < l.end.line) { out << l.begin.line << "-" << l.end.line << ": " << typestr << msg << std::endl << color_end; return; } // For a single line error the format is: // // ::-: ERROR: // // // // E.g. // // file.bt:1:10-20: error: // i:s:1 /1 < "str"/ // ~~~~~~~~~~ out << l.begin.line << ":" << l.begin.column << "-" << l.end.column; out << ": " << typestr << msg << std::endl << color_end; // for bpftrace::position, valid line# starts from 1 std::string srcline = get_source_line(l.begin.line - 1); if (srcline == "") { return; } // To get consistent printing all tabs will be replaced with 4 spaces for (auto c : srcline) { if (c == '\t') out << " "; else out << c; } out << std::endl; for (unsigned int x = 0; x < srcline.size() && x < (static_cast(l.end.column) - 1); x++) { char marker = (x < (static_cast(l.begin.column) - 1)) ? ' ' : '~'; if (srcline[x] == '\t') { out << std::string(4, marker); } else { out << marker; } } out << std::endl; } LogStream::LogStream(const std::string& file, int line, LogType type, std::ostream& out) : sink_(Log::get()), type_(type), loc_(std::nullopt), out_(out), log_file_(file), log_line_(line) { } LogStream::LogStream(const std::string& file, int line, LogType type, std::optional loc, std::ostream& out) : sink_(Log::get()), type_(type), loc_(loc), out_(out), log_file_(file), log_line_(line) { } LogStream::~LogStream() { #ifdef FUZZ // When fuzzing, we don't want to output error messages. However, some // function uses a error message length to determine whether if an error // occur. So, we cannot simply DISABLE_LOG(ERROR). Instead, here, we don't // output error messages to stderr. if (sink_.is_enabled(type_) && (type_ != LogType::ERROR || (&out_ != &std::cout && &out_ != &std::cerr))) { #else if (sink_.is_enabled(type_)) { #endif auto msg = buf_.str(); if (type_ == LogType::DEBUG) msg = internal_location() + msg; sink_.take_input(type_, loc_, out_, std::move(msg)); } } std::string LogStream::internal_location() { std::ostringstream ss; ss << "[" << log_file_ << ":" << log_line_ << "] "; return ss.str(); } [[noreturn]] LogStreamBug::~LogStreamBug() { sink_.take_input(type_, loc_, out_, internal_location() + buf_.str()); abort(); } }; // namespace bpftrace bpftrace-0.23.2/src/log.h000066400000000000000000000100231477746507000151220ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "location.hh" namespace bpftrace { namespace LogColor { constexpr const char* RESET = "\033[0m"; constexpr const char* RED = "\033[31m"; constexpr const char* YELLOW = "\033[33m"; constexpr const char* DEFAULT = ""; } // namespace LogColor // clang-format off enum class LogType { DEBUG, V1, HINT, WARNING, ERROR, BUG, }; // clang-format on class Log { public: static Log& get(); void take_input(LogType type, const std::optional& loc, std::ostream& out, std::string&& input); inline void set_source(std::string_view filename, std::string_view source) { src_ = source; filename_ = filename; } inline const std::string& get_source() { return src_; } const std::string get_source_line(unsigned int n); // Can only construct with get() Log(const Log& other) = delete; Log& operator=(const Log& other) = delete; inline void enable(LogType type) { enabled_map_[type] = true; } inline void disable(LogType type) { assert(type != LogType::BUG && type != LogType::ERROR); enabled_map_[type] = false; } inline bool is_enabled(LogType type) { return enabled_map_[type]; } inline void set_colorize(bool is_colorize) { is_colorize_ = is_colorize; } private: Log(); ~Log() = default; std::string src_; std::string filename_; void log_with_location(LogType, const location&, std::ostream&, const std::string&); std::string log_format_output(LogType, std::string&&); std::unordered_map enabled_map_; bool is_colorize_ = false; }; class LogStream { public: LogStream(const std::string& file, int line, LogType type, std::ostream& out = std::cerr); LogStream(const std::string& file, int line, LogType type, std::optional loc, std::ostream& out = std::cerr); template LogStream& operator<<(const T& v) { if (sink_.is_enabled(type_)) buf_ << v; return *this; } virtual ~LogStream(); protected: std::string internal_location(); Log& sink_; LogType type_; const std::optional loc_; std::ostream& out_; std::string log_file_; int log_line_; std::ostringstream buf_; }; 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, std::optional 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."; // 3. LOG(ERROR, call.loc, std::cerr) << "this is a semantic error"; // Note: LogType::DEBUG will prepend __FILE__ and __LINE__ to the debug message // clang-format off #define LOGSTREAM_COMMON(...) bpftrace::LogStream(__FILE__, __LINE__, __VA_ARGS__) #define LOGSTREAM_DEBUG(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_V1(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_HINT(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_WARNING(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_ERROR(...) LOGSTREAM_COMMON(__VA_ARGS__) #define LOGSTREAM_BUG(...) bpftrace::LogStreamBug(__FILE__, __LINE__, __VA_ARGS__) // clang-format on #define LOG(type, ...) LOGSTREAM_##type(bpftrace::LogType::type, ##__VA_ARGS__) #define DISABLE_LOG(type) bpftrace::Log::get().disable(LogType::type) #define ENABLE_LOG(type) bpftrace::Log::get().enable(LogType::type) }; // namespace bpftrace bpftrace-0.23.2/src/main.cpp000066400000000000000000000755511477746507000156410ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "aot/aot.h" #include "ast/pass_manager.h" #include "ast/passes/codegen_llvm.h" #include "ast/passes/config_analyser.h" #include "ast/passes/field_analyser.h" #include "ast/passes/portability_analyser.h" #include "ast/passes/resource_analyser.h" #include "ast/passes/return_path_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpffeature.h" #include "bpftrace.h" #include "btf.h" #include "build_info.h" #include "child.h" #include "clang_parser.h" #include "config.h" #include "driver.h" #include "lockdown.h" #include "log.h" #include "output.h" #include "probe_matcher.h" #include "procmon.h" #include "run_bpftrace.h" #include "tracepoint_format_parser.h" #include "utils.h" #include "version.h" using namespace bpftrace; namespace { enum class OutputBufferConfig { UNSET = 0, LINE, FULL, NONE, }; enum class TestMode { UNSET = 0, CODEGEN, }; enum class BuildMode { // Compile script and run immediately DYNAMIC = 0, // 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, NO_FEATURE, DEBUG, DRY_RUN, }; } // namespace void usage(std::ostream& out) { // clang-format off out << "USAGE:" << std::endl; out << " bpftrace [options] filename" << std::endl; out << " bpftrace [options] - " << std::endl; out << " bpftrace [options] -e 'program'" << std::endl; out << std::endl; out << "OPTIONS:" << std::endl; out << " -B MODE output buffering mode ('line', 'full', 'none')" << std::endl; out << " -f FORMAT output format ('text', 'json')" << std::endl; out << " -o file redirect bpftrace output to file" << std::endl; out << " -e 'program' execute this program" << std::endl; out << " -h, --help show this help message" << std::endl; out << " -I DIR add the directory to the include search path" << std::endl; out << " --include FILE add an #include file before preprocessing" << std::endl; out << " -l [search|filename]" << std::endl; out << " list kernel probes or probes in a program" << std::endl; out << " -p PID enable USDT probes on PID" << std::endl; out << " -c 'CMD' run CMD and enable USDT probes on resulting process" << std::endl; out << " --usdt-file-activation" << std::endl; out << " activate usdt semaphores based on file path" << std::endl; out << " --unsafe allow unsafe/destructive functionality" << std::endl; out << " -q keep messages quiet" << std::endl; out << " --info Print information about kernel BPF support" << std::endl; out << " -k emit a warning when probe read helpers return an error" << std::endl; out << " -V, --version bpftrace version" << std::endl; out << " --no-warnings disable all warning messages" << std::endl; out << std::endl; out << "TROUBLESHOOTING OPTIONS:" << std::endl; out << " -v verbose messages" << std::endl; out << " --dry-run terminate execution right after attaching all the probes" << std::endl; out << " -d STAGE debug info for various stages of bpftrace execution" << std::endl; out << " ('all', 'ast', 'codegen', 'codegen-opt', 'dis', 'libbpf', 'verifier')" << std::endl; out << " --emit-elf FILE (dry run) generate ELF file with bpf programs and write to FILE" << std::endl; out << " --emit-llvm FILE write LLVM IR to FILE.original.ll and FILE.optimized.ll" << std::endl; out << std::endl; out << "ENVIRONMENT:" << std::endl; out << " BPFTRACE_BTF [default: none] BTF file" << std::endl; out << " BPFTRACE_CACHE_USER_SYMBOLS [default: auto] enable user symbol cache" << std::endl; out << " BPFTRACE_COLOR [default: auto] enable log output colorization" << std::endl; out << " BPFTRACE_CPP_DEMANGLE [default: 1] enable C++ symbol demangling" << std::endl; out << " BPFTRACE_DEBUG_OUTPUT [default: 0] enable bpftrace's internal debugging outputs" << std::endl; out << " BPFTRACE_KERNEL_BUILD [default: /lib/modules/$(uname -r)] kernel build directory" << std::endl; out << " BPFTRACE_KERNEL_SOURCE [default: /lib/modules/$(uname -r)] kernel headers directory" << std::endl; out << " BPFTRACE_LAZY_SYMBOLICATION [default: 0] symbolicate lazily/on-demand" << std::endl; out << " BPFTRACE_LOG_SIZE [default: 1000000] log size in bytes" << std::endl; out << " BPFTRACE_MAX_BPF_PROGS [default: 1024] max number of generated BPF programs" << std::endl; out << " BPFTRACE_MAX_CAT_BYTES [default: 10k] maximum bytes read by cat builtin" << std::endl; out << " BPFTRACE_MAX_MAP_KEYS [default: 4096] max keys in a map" << std::endl; out << " BPFTRACE_MAX_PROBES [default: 1024] max number of probes" << std::endl; out << " BPFTRACE_MAX_STRLEN [default: 1024] bytes on BPF stack per str()" << std::endl; out << " BPFTRACE_MAX_TYPE_RES_ITERATIONS [default: 0] number of levels of nested field accesses for tracepoint args" << std::endl; out << " BPFTRACE_PERF_RB_PAGES [default: 64] pages per CPU to allocate for ring buffer" << std::endl; out << " BPFTRACE_STACK_MODE [default: bpftrace] Output format for ustack and kstack builtins" << std::endl; out << " BPFTRACE_STR_TRUNC_TRAILER [default: '..'] string truncation trailer" << std::endl; out << " BPFTRACE_VMLINUX [default: none] vmlinux path used for kernel symbol resolution" << std::endl; out << std::endl; out << "EXAMPLES:" << std::endl; out << "bpftrace -l '*sleep*'" << std::endl; out << " list probes containing \"sleep\"" << std::endl; out << R"(bpftrace -e 'kprobe:do_nanosleep { printf("PID %d sleeping...\n", pid); }')" << std::endl; out << " trace processes calling sleep" << std::endl; out << "bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'" << std::endl; out << " count syscalls by process name" << std::endl; // clang-format on } static void enforce_infinite_rlimit() { struct rlimit rl = {}; int err; rl.rlim_max = RLIM_INFINITY; rl.rlim_cur = rl.rlim_max; err = setrlimit(RLIMIT_MEMLOCK, &rl); if (err) LOG(WARNING) << std::strerror(err) << ": couldn't set RLIMIT_MEMLOCK for " << "bpftrace. If your program is not loading, you can try " << "\"ulimit -l 8192\" to fix the problem"; } static void info(BPFnofeature no_feature) { struct utsname utsname; uname(&utsname); std::cout << "System" << std::endl << " OS: " << utsname.sysname << " " << utsname.release << " " << utsname.version << std::endl << " Arch: " << utsname.machine << std::endl; std::cout << std::endl; std::cout << BuildInfo::report(); std::cout << std::endl; std::cout << BPFfeature(no_feature).report(); } static std::optional get_delta_with_boottime(int clock_type) { std::optional ret = std::nullopt; long lowest_delta = std::numeric_limits::max(); // Run the "triple vdso sandwich" 5 times, taking the result from the // iteration with the lowest delta between first and last clock_gettime() // calls. for (int i = 0; i < 5; ++i) { struct timespec before, after, boottime; long delta; if (::clock_gettime(clock_type, &before)) continue; if (::clock_gettime(CLOCK_BOOTTIME, &boottime)) continue; if (::clock_gettime(clock_type, &after)) continue; // There's no way 3 VDSO calls should take more than 1s. We'll // also ignore the case where we cross a 1s boundary b/c that // can only happen once and we're running this loop 5 times. // This helps keep the math simple. if (before.tv_sec != after.tv_sec) continue; delta = after.tv_nsec - before.tv_nsec; // Time went backwards if (delta < 0) continue; // Lowest delta seen so far, compute boot realtime and store it if (delta < lowest_delta) { struct timespec delta_with_boottime; long nsec_avg = (before.tv_nsec + after.tv_nsec) / 2; if (nsec_avg - boottime.tv_nsec < 0) { delta_with_boottime.tv_sec = after.tv_sec - boottime.tv_sec - 1; delta_with_boottime.tv_nsec = nsec_avg - boottime.tv_nsec + 1e9; } else { delta_with_boottime.tv_sec = after.tv_sec - boottime.tv_sec; delta_with_boottime.tv_nsec = nsec_avg - boottime.tv_nsec; } lowest_delta = delta; ret = delta_with_boottime; } } if (ret && lowest_delta >= 1e5) LOG(WARNING) << (lowest_delta / 1e3) << "us skew detected when calculating boot time. strftime() " "builtin may be inaccurate"; return ret; } static std::optional get_boottime() { return get_delta_with_boottime(CLOCK_REALTIME); } static std::optional get_delta_taitime() { return get_delta_with_boottime(CLOCK_TAI); } static void parse_env(BPFtrace& bpftrace) { ConfigSetter config_setter(bpftrace.config_, ConfigSource::env_var); get_uint64_env_var("BPFTRACE_MAX_STRLEN", [&](uint64_t x) { config_setter.set(ConfigKeyInt::max_strlen, x); }); get_uint64_env_var("BPFTRACE_STRLEN", [&](uint64_t x) { LOG(WARNING) << "BPFTRACE_STRLEN is deprecated. Use " "BPFTRACE_MAX_STRLEN instead."; config_setter.set(ConfigKeyInt::max_strlen, x); }); if (const char* env_p = std::getenv("BPFTRACE_STR_TRUNC_TRAILER")) config_setter.set(ConfigKeyString::str_trunc_trailer, std::string(env_p)); get_bool_env_var("BPFTRACE_CPP_DEMANGLE", [&](bool x) { config_setter.set(ConfigKeyBool::cpp_demangle, x); }); get_bool_env_var("BPFTRACE_DEBUG_OUTPUT", [&](bool x) { bpftrace.debug_output_ = x; }); get_bool_env_var("BPFTRACE_LAZY_SYMBOLICATION", [&](bool x) { config_setter.set(ConfigKeyBool::lazy_symbolication, x); }); get_uint64_env_var("BPFTRACE_MAX_MAP_KEYS", [&](uint64_t x) { config_setter.set(ConfigKeyInt::max_map_keys, x); }); get_uint64_env_var("BPFTRACE_MAX_PROBES", [&](uint64_t x) { config_setter.set(ConfigKeyInt::max_probes, x); }); get_uint64_env_var("BPFTRACE_MAX_BPF_PROGS", [&](uint64_t x) { config_setter.set(ConfigKeyInt::max_bpf_progs, x); }); get_uint64_env_var("BPFTRACE_LOG_SIZE", [&](uint64_t x) { config_setter.set(ConfigKeyInt::log_size, x); }); get_uint64_env_var("BPFTRACE_PERF_RB_PAGES", [&](uint64_t x) { config_setter.set(ConfigKeyInt::perf_rb_pages, x); }); get_uint64_env_var("BPFTRACE_MAX_TYPE_RES_ITERATIONS", [&](uint64_t x) { config_setter.set(ConfigKeyInt::max_type_res_iterations, x); }); get_uint64_env_var("BPFTRACE_MAX_CAT_BYTES", [&](uint64_t x) { config_setter.set(ConfigKeyInt::max_cat_bytes, x); }); if (const char* env_p = std::getenv("BPFTRACE_CACHE_USER_SYMBOLS")) { const std::string s(env_p); if (!config_setter.set_user_symbol_cache_type(s)) exit(1); } get_uint64_env_var("BPFTRACE_MAX_AST_NODES", [&](uint64_t x) { bpftrace.max_ast_nodes_ = x; }); if (const char* stack_mode = std::getenv("BPFTRACE_STACK_MODE")) { if (!config_setter.set_stack_mode(stack_mode)) exit(1); } get_bool_env_var("BPFTRACE_NO_CPP_DEMANGLE", [&](bool x) { LOG(WARNING) << "BPFTRACE_NO_CPP_DEMANGLE is deprecated. Use " "BPFTRACE_CPP_DEMANGLE=0 instead."; config_setter.set(ConfigKeyBool::cpp_demangle, !x); }); get_uint64_env_var("BPFTRACE_CAT_BYTES_MAX", [&](uint64_t x) { LOG(WARNING) << "BPFTRACE_CAT_BYTES_MAX is deprecated. Use " "BPFTRACE_MAX_CAT_BYTES instead."; config_setter.set(ConfigKeyInt::max_cat_bytes, x); }); get_uint64_env_var("BPFTRACE_MAP_KEYS_MAX", [&](uint64_t x) { LOG(WARNING) << "BPFTRACE_MAP_KEYS_MAX is deprecated. Use " "BPFTRACE_MAX_MAP_KEYS instead."; config_setter.set(ConfigKeyInt::max_map_keys, x); }); get_bool_env_var("BPFTRACE_USE_BLAZESYM", [&](bool x) { #ifndef HAVE_BLAZESYM if (x) { LOG(ERROR) << "BPFTRACE_USE_BLAZESYM requires blazesym support enabled " "during build."; exit(1); } #endif config_setter.set(ConfigKeyBool::use_blazesym, x); }); } [[nodiscard]] std::optional parse( BPFtrace& bpftrace, const std::string& name, const std::string& program, const std::vector& include_dirs, const std::vector& include_files) { Driver driver(bpftrace); driver.source(name, program); int err; err = driver.parse(); if (err) return std::nullopt; bpftrace.parse_btf(driver.list_modules()); ast::FieldAnalyser fields(driver.ctx, bpftrace); err = fields.analyse(); if (err) return std::nullopt; if (TracepointFormatParser::parse(driver.ctx, bpftrace) == false) return std::nullopt; // NOTE(mmarchini): if there are no C definitions, clang parser won't run to // 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.ctx.root->c_definitions.empty()) driver.ctx.root->c_definitions = "#define __BPFTRACE_DUMMY__"; bool should_clang_parse = !(driver.ctx.root->c_definitions.empty() && bpftrace.btf_set_.empty()); if (should_clang_parse) { ClangParser clang; std::string ksrc, kobj; struct utsname utsname; std::vector extra_flags; uname(&utsname); bool found_kernel_headers = get_kernel_dirs(utsname, ksrc, kobj); if (found_kernel_headers) extra_flags = get_kernel_cflags( utsname.machine, ksrc, kobj, bpftrace.kconfig); for (auto dir : include_dirs) { extra_flags.push_back("-I"); extra_flags.push_back(dir); } for (auto file : include_files) { extra_flags.push_back("-include"); extra_flags.push_back(file); } if (!clang.parse(driver.ctx.root, bpftrace, extra_flags)) { if (!found_kernel_headers) { LOG(WARNING) << "Could not find kernel headers in " << ksrc << " / " << kobj << ". To specify a particular path to kernel headers, set the env " << "variables BPFTRACE_KERNEL_SOURCE and, optionally, " << "BPFTRACE_KERNEL_BUILD if the kernel was built in a different " << "directory than its source. You can also point the variable to " << "a directory with built-in headers extracted from the following " << "snippet:\nmodprobe kheaders && tar -C -xf " << "/sys/kernel/kheaders.tar.xz"; } return {}; } } err = driver.parse(); if (err) return {}; return std::move(driver.ctx); } ast::PassManager CreateDynamicPM() { ast::PassManager pm; pm.AddPass(ast::CreateConfigPass()); pm.AddPass(ast::CreateSemanticPass()); pm.AddPass(ast::CreateResourcePass()); pm.AddPass(ast::CreateReturnPathPass()); return pm; } ast::PassManager CreateAotPM() { ast::PassManager pm; pm.AddPass(ast::CreateSemanticPass()); pm.AddPass(ast::CreatePortabilityPass()); pm.AddPass(ast::CreateResourcePass()); pm.AddPass(ast::CreateReturnPathPass()); return pm; } struct Args { std::string pid_str; std::string cmd_str; bool listing = false; bool safe_mode = true; bool usdt_file_activation = false; int helper_check_level = 1; bool no_warnings = false; TestMode test_mode = TestMode::UNSET; 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; BPFnofeature no_feature; OutputBufferConfig obc = OutputBufferConfig::UNSET; BuildMode build_mode = BuildMode::DYNAMIC; std::vector include_dirs; std::vector include_files; std::vector params; std::vector debug_stages; }; static bool parse_debug_stages(const std::string& arg) { auto stages = split_string(arg, ',', /* remove_empty= */ true); for (const auto& stage : stages) { if (debug_stages.contains(stage)) { bt_debug.insert(debug_stages.at(stage)); } else if (stage == "all") { for (const auto& [_, s] : debug_stages) bt_debug.insert(s); } else { LOG(ERROR) << "USAGE: invalid option for -d: " << stage; return false; } } return true; } Args parse_args(int argc, char* argv[]) { Args args; const char* const short_options = "d:bB:f:e:hlp:vqc:Vo:I:k"; option long_options[] = { 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{ "no-feature", required_argument, nullptr, Options::NO_FEATURE }, option{ "debug", required_argument, nullptr, Options::DEBUG }, option{ "dry-run", no_argument, nullptr, Options::DRY_RUN }, option{ nullptr, 0, nullptr, 0 }, // Must be last }; int c; bool has_k = false; while ((c = getopt_long(argc, argv, short_options, long_options, nullptr)) != -1) { switch (c) { case Options::INFO: // --info check_is_root(); info(args.no_feature); exit(0); break; case Options::EMIT_ELF: // --emit-elf args.output_elf = optarg; break; case Options::EMIT_LLVM: args.output_llvm = optarg; break; case Options::NO_WARNING: // --no-warnings DISABLE_LOG(WARNING); args.no_warnings = true; args.helper_check_level = 0; break; case Options::TEST: // --test if (std::strcmp(optarg, "codegen") == 0) args.test_mode = TestMode::CODEGEN; else { LOG(ERROR) << "USAGE: --test can only be 'codegen'."; exit(1); } break; case Options::AOT: // --aot args.aot = optarg; args.build_mode = BuildMode::AHEAD_OF_TIME; break; case Options::NO_FEATURE: // --no-feature if (args.no_feature.parse(optarg)) { LOG(ERROR) << "USAGE: --no-feature can only have values " "'kprobe_multi,uprobe_multi'."; exit(1); } break; case Options::DRY_RUN: dry_run = true; break; case 'o': args.output_file = optarg; break; case 'd': case Options::DEBUG: if (!parse_debug_stages(optarg)) exit(1); break; case 'q': bt_quiet = true; break; case 'v': ENABLE_LOG(V1); bt_verbose = true; break; case 'B': if (std::strcmp(optarg, "line") == 0) { args.obc = OutputBufferConfig::LINE; } else if (std::strcmp(optarg, "full") == 0) { args.obc = OutputBufferConfig::FULL; } else if (std::strcmp(optarg, "none") == 0) { args.obc = OutputBufferConfig::NONE; } else { LOG(ERROR) << "USAGE: -B must be either 'line', 'full', or 'none'."; exit(1); } break; case 'f': args.output_format = optarg; break; case 'e': args.script = optarg; break; case 'p': args.pid_str = optarg; break; case 'I': args.include_dirs.push_back(optarg); break; case Options::INCLUDE: args.include_files.push_back(optarg); break; case 'l': args.listing = true; break; case 'c': args.cmd_str = optarg; break; case Options::USDT_SEMAPHORE: args.usdt_file_activation = true; break; case Options::UNSAFE: args.safe_mode = false; break; case 'b': case Options::BTF: break; case 'h': case Options::HELP: usage(std::cout); exit(0); case 'V': case Options::VERSION: std::cout << "bpftrace " << BPFTRACE_VERSION << std::endl; exit(0); case 'k': if (has_k) { LOG(ERROR) << "USAGE: -kk has been deprecated. Use a single -k for " "runtime warnings for errors in map " "lookups and probe reads."; exit(1); } if (!args.no_warnings) { args.helper_check_level = 2; } has_k = true; break; default: usage(std::cerr); exit(1); } } if (argc == 1) { usage(std::cerr); exit(1); } if (!args.cmd_str.empty() && !args.pid_str.empty()) { LOG(ERROR) << "USAGE: Cannot use both -c and -p."; usage(std::cerr); exit(1); } // Difficult to serialize flex generated types if (args.helper_check_level == 2 && args.build_mode == BuildMode::AHEAD_OF_TIME) { LOG(ERROR) << "Cannot use -k with --aot"; exit(1); } if (args.listing) { // Expect zero or one positional arguments if (optind == argc) { args.search = "*:*"; } else if (optind == argc - 1) { std::string_view val(argv[optind]); if (std::filesystem::exists(val)) { args.filename = val; } else { if (val == "*") { args.search = "*:*"; } else { args.search = val; } } optind++; } else { usage(std::cerr); 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; } bool is_colorize() { const char* color_env = std::getenv("BPFTRACE_COLOR"); if (!color_env) { return isatty(STDOUT_FILENO) && isatty(STDERR_FILENO); } std::string_view mode(color_env); if (mode == "always") { return true; } else if (mode == "never") { return false; } else { if (mode != "auto") { LOG(WARNING) << "Invalid env value! The valid values of `BPFTRACE_COLOR` " "are [auto|always|never]. The current value is " << mode << "!"; } return isatty(STDOUT_FILENO) && isatty(STDERR_FILENO); } } int main(int argc, char* argv[]) { Log::get().set_colorize(is_colorize()); const Args args = parse_args(argc, argv); std::ostream* os = &std::cout; std::ofstream outputstream; if (!args.output_file.empty()) { outputstream.open(args.output_file); if (outputstream.fail()) { LOG(ERROR) << "Failed to open output file: \"" << args.output_file << "\": " << strerror(errno); exit(1); } os = &outputstream; } std::unique_ptr output; if (args.output_format.empty() || args.output_format == "text") { output = std::make_unique(*os); } else if (args.output_format == "json") { output = std::make_unique(*os); } else { LOG(ERROR) << "Invalid output format \"" << args.output_format << "\"\n" << "Valid formats: 'text', 'json'"; exit(1); } switch (args.obc) { case OutputBufferConfig::UNSET: case OutputBufferConfig::LINE: std::setvbuf(stdout, nullptr, _IOLBF, BUFSIZ); break; case OutputBufferConfig::FULL: std::setvbuf(stdout, nullptr, _IOFBF, BUFSIZ); break; case OutputBufferConfig::NONE: std::setvbuf(stdout, nullptr, _IONBF, BUFSIZ); break; } libbpf_set_print(libbpf_print); Config config = Config(!args.cmd_str.empty()); BPFtrace bpftrace(std::move(output), args.no_feature, config); parse_env(bpftrace); 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(); bpftrace.delta_taitime_ = get_delta_taitime(); if (!args.pid_str.empty()) { std::string errmsg; auto maybe_pid = parse_pid(args.pid_str, errmsg); if (!maybe_pid.has_value()) { LOG(ERROR) << "Failed to parse pid: " + errmsg; exit(1); } try { bpftrace.procmon_ = std::make_unique(*maybe_pid); } catch (const std::exception& e) { LOG(ERROR) << e.what(); exit(1); } } if (!args.cmd_str.empty()) { bpftrace.cmd_ = args.cmd_str; try { bpftrace.child_ = std::make_unique(args.cmd_str); } catch (const std::runtime_error& e) { LOG(ERROR) << "Failed to fork child: " << e.what(); exit(1); } } // Listing probes when there is no program if (args.listing && args.script.empty() && args.filename.empty()) { check_is_root(); if (is_type_name(args.search)) { // Print structure definitions bpftrace.parse_btf({}); bpftrace.probe_matcher_->list_structs(args.search); return 0; } if (args.search.find(".") != std::string::npos && args.search.find_first_of(":*") == std::string::npos) { LOG(WARNING) << "It appears that \'" << args.search << "\' is a filename but the file does not exist. Treating \'" << args.search << "\' as a search pattern."; } Driver driver(bpftrace); driver.listing_ = true; driver.source("stdin", args.search); int err = driver.parse(); if (err) return err; bpftrace.parse_btf(driver.list_modules()); ast::SemanticAnalyser semantics(driver.ctx, bpftrace, false, true); err = semantics.analyse(); if (err) return err; bpftrace.probe_matcher_->list_probes(driver.ctx.root); return 0; } std::string filename; std::string program; if (!args.filename.empty()) { std::stringstream buf; if (args.filename == "-") { std::string line; while (std::getline(std::cin, line)) { // Note we may add an extra newline if the input doesn't end in a new // line. This should not matter because bpftrace (the language) is not // whitespace sensitive. buf << line << std::endl; } filename = "stdin"; program = buf.str(); } else { std::ifstream file(args.filename); if (file.fail()) { LOG(ERROR) << "failed to open file '" << args.filename << "': " << std::strerror(errno); exit(1); } filename = args.filename; program = buf.str(); buf << file.rdbuf(); program = buf.str(); } } else { // Script is provided as a command line argument filename = "stdin"; program = args.script; } for (const auto& param : args.params) { bpftrace.add_param(param); } check_is_root(); auto lockdown_state = lockdown::detect(); if (lockdown_state == lockdown::LockdownState::Confidentiality) { lockdown::emit_warning(std::cerr); return 1; } // FIXME (mmarchini): maybe we don't want to always enforce an infinite // rlimit? enforce_infinite_rlimit(); auto ast_ctx = parse( bpftrace, filename, program, args.include_dirs, args.include_files); if (!ast_ctx) return 1; if (args.listing) { bpftrace.probe_matcher_->list_probes(ast_ctx->root); return 0; } ast::PassContext ctx(bpftrace, *ast_ctx); ast::PassManager pm; switch (args.build_mode) { case BuildMode::DYNAMIC: pm = CreateDynamicPM(); break; case BuildMode::AHEAD_OF_TIME: if (bpftrace.has_dwarf_data()) { // See #3392 to learn why AOT does not yet support uprobe+DebugInfo. LOG(ERROR) << "AOT does not yet support uprobe probes using DebugInfo."; if (std::getenv("__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED")) std::cout << "__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED" << std::endl; } pm = CreateAotPM(); break; } bpftrace.fentry_recursion_check(ast_ctx->root); auto pmresult = pm.Run(ctx); if (!pmresult.Ok()) return 1; ast::CodegenLLVM llvm(ctx.ast_ctx, bpftrace); BpfBytecode bytecode; try { llvm.generate_ir(); if (bt_debug.find(DebugStage::Codegen) != bt_debug.end()) { std::cout << "LLVM IR before optimization\n"; std::cout << "---------------------------\n\n"; llvm.DumpIR(); } if (!args.output_llvm.empty()) { llvm.DumpIR(args.output_llvm + ".original.ll"); } bool verify_llvm_ir = false; get_bool_env_var("BPFTRACE_VERIFY_LLVM_IR", [&](bool x) { verify_llvm_ir = x; }); if (verify_llvm_ir && !llvm.verify()) { LOG(ERROR) << "Verification of generated LLVM IR failed"; exit(1); } llvm.optimize(); if (bt_debug.find(DebugStage::CodegenOpt) != bt_debug.end()) { std::cout << "\nLLVM IR after optimization\n"; std::cout << "----------------------------\n\n"; llvm.DumpIR(); } if (!args.output_llvm.empty()) { llvm.DumpIR(args.output_llvm + ".optimized.ll"); } if (!args.output_elf.empty()) { llvm.emit_elf(args.output_elf); return 0; } if (args.build_mode == BuildMode::AHEAD_OF_TIME) { llvm::SmallVector aot_output; llvm::raw_svector_ostream aot_os(aot_output); llvm.emit(aot_os); return aot::generate( bpftrace.resources, args.aot, aot_output.data(), aot_output.size()); } bool disassemble = bt_debug.find(DebugStage::Disassemble) != bt_debug.end(); bytecode = llvm.emit(disassemble); } catch (const std::system_error& ex) { LOG(ERROR) << "failed to write elf: " << ex.what(); return 1; } catch (const std::exception& ex) { LOG(ERROR) << "Failed to compile: " << ex.what(); return 1; } if (args.test_mode == TestMode::CODEGEN) return 0; return run_bpftrace(bpftrace, bytecode); } bpftrace-0.23.2/src/output.cpp000066400000000000000000001100551477746507000162420ustar00rootroot00000000000000#include "output.h" #include "ast/async_event_types.h" #include "bpftrace.h" #include "log.h" #include "utils.h" #include namespace libbpf { #define __BPF_NAME_FN(x) #x const char *bpf_func_name[] = { __BPF_FUNC_MAPPER(__BPF_NAME_FN) }; #undef __BPF_NAME_FN } // namespace libbpf namespace bpftrace { namespace { bool is_quoted_type(const SizedType &ty) { switch (ty.GetTy()) { case Type::buffer: case Type::cgroup_path_t: case Type::inet: case Type::kstack_t: case Type::ksym_t: case Type::none: case Type::strerror_t: case Type::string: case Type::timestamp: case Type::username: case Type::ustack_t: case Type::usym_t: return true; case Type::array: case Type::avg_t: case Type::count_t: case Type::hist_t: case Type::integer: case Type::lhist_t: case Type::mac_address: case Type::max_t: case Type::min_t: case Type::pointer: case Type::reference: case Type::record: case Type::stack_mode: case Type::stats_t: case Type::sum_t: case Type::timestamp_mode: case Type::tuple: case Type::voidtype: return false; } return false; } } // namespace std::ostream &operator<<(std::ostream &out, MessageType type) { switch (type) { case MessageType::map: out << "map"; break; case MessageType::value: out << "value"; break; case MessageType::hist: out << "hist"; break; case MessageType::stats: out << "stats"; break; case MessageType::printf: out << "printf"; break; case MessageType::time: out << "time"; break; case MessageType::cat: out << "cat"; break; case MessageType::join: out << "join"; break; case MessageType::syscall: out << "syscall"; break; case MessageType::attached_probes: out << "attached_probes"; break; case MessageType::lost_events: out << "lost_events"; break; default: out << "?"; } return out; } // Translate the index into the starting value for the corresponding interval. // Each power of 2 is mapped into N = 2**k intervals, each of size // S = 1 << ((index >> k) - 1), and starting at S * N. // The last k bits of index indicate which interval we want. // // For example, if k = 2 and index = 0b11011 (27) we have: // - N = 2**2 = 4; // - interval size S is 1 << ((0b11011 >> 2) - 1) = 1 << (6 - 1) = 32 // - starting value is S * N = 128 // - the last 2 bits 11 indicate the third interval so the // starting value is 128 + 32*3 = 224 std::string TextOutput::hist_index_label(uint32_t index, uint32_t k) { const uint32_t n = (1 << k), interval = index & (n - 1); assert(index >= n); uint32_t power = (index >> k) - 1; // Choose the suffix for the largest power of 2^10 const uint32_t decade = power / 10; const char suffix = "\0KMGTPE"[decade]; power -= 10 * decade; std::ostringstream label; label << (1 << power) * (n + interval); if (suffix) label << suffix; return label.str(); } std::string TextOutput::lhist_index_label(int number, int step) { constexpr int kilo = 1024; constexpr int mega = 1024 * 1024; if (step % kilo != 0) return std::to_string(number); std::ostringstream label; if (number == 0) { label << number; } else if (number % mega == 0) { label << number / mega << 'M'; } else if (number % kilo == 0) { label << number / kilo << 'K'; } else { label << number; } return label.str(); } void Output::hist_prepare(const std::vector &values, int &min_index, int &max_index, int &max_value) const { min_index = -1; max_index = -1; max_value = 0; for (size_t i = 0; i < values.size(); i++) { int v = values.at(i); if (v > 0) { if (min_index == -1) min_index = i; max_index = i; } if (v > max_value) max_value = v; } } void Output::lhist_prepare(const std::vector &values, int min, int max, int step, int &max_index, int &max_value, int &buckets, int &start_value, int &end_value) const { max_index = -1; max_value = 0; buckets = (max - min) / step; // excluding lt and gt buckets for (size_t i = 0; i < values.size(); i++) { int v = values.at(i); if (v != 0) max_index = i; if (v > max_value) max_value = v; } if (max_index == -1) return; // trim empty values start_value = -1; end_value = 0; for (unsigned int i = 0; i <= static_cast(buckets) + 1; i++) { if (values.at(i) > 0) { if (start_value == -1) { start_value = i; } end_value = i; } } if (start_value == -1) { start_value = 0; } } std::string Output::get_helper_error_msg(int func_id, int retcode) const { std::string msg; if (func_id == libbpf::BPF_FUNC_map_update_elem && retcode == -E2BIG) { msg = "Map full; can't update element. Try increasing max_map_keys config"; } else if (func_id == libbpf::BPF_FUNC_map_delete_elem && retcode == -ENOENT) { msg = "Can't delete map element because it does not exist."; } // bpftrace sets the return code to 0 for map_lookup_elem failures // which is why we're not also checking the retcode else if (func_id == libbpf::BPF_FUNC_map_lookup_elem) { msg = "Can't lookup map element because it does not exist."; } else { msg = strerror(-retcode); } return msg; } std::string Output::value_to_str(BPFtrace &bpftrace, const SizedType &type, const std::vector &value, bool is_per_cpu, uint32_t div, bool is_map_key) const { uint32_t nvalues = is_per_cpu ? bpftrace.ncpus_ : 1; switch (type.GetTy()) { case Type::kstack_t: { return bpftrace.get_stack(read_data(value.data()), read_data(value.data() + 8), -1, -1, false, type.stack_type, 8); } case Type::ustack_t: { return bpftrace.get_stack(read_data(value.data()), read_data(value.data() + 8), read_data(value.data() + 16), read_data(value.data() + 20), true, type.stack_type, 8); } case Type::ksym_t: { return bpftrace.resolve_ksym(read_data(value.data())); } case Type::usym_t: { return bpftrace.resolve_usym(read_data(value.data()), read_data(value.data() + 8), read_data(value.data() + 12)); } case Type::inet: { return bpftrace.resolve_inet(read_data(value.data()), static_cast(value.data() + 8)); } case Type::username: { return bpftrace.resolve_uid(read_data(value.data())); } case Type::buffer: { return bpftrace.resolve_buf( reinterpret_cast(value.data())->content, reinterpret_cast(value.data())->length); } case Type::string: { auto p = reinterpret_cast(value.data()); return std::string(p, strnlen(p, type.GetSize())); } case Type::array: { size_t elem_size = type.GetElementTy()->GetSize(); std::vector elems; for (size_t i = 0; i < type.GetNumElements(); i++) { std::vector elem_data(value.begin() + i * elem_size, value.begin() + (i + 1) * elem_size); elems.push_back(value_to_str(bpftrace, *type.GetElementTy(), elem_data, is_per_cpu, div, is_map_key)); } return array_to_str(elems); } case Type::record: { std::vector elems; for (auto &field : type.GetFields()) { std::vector elem_data(value.begin() + field.offset, value.begin() + field.offset + field.type.GetSize()); elems.push_back(field_to_str( field.name, value_to_str( bpftrace, field.type, elem_data, is_per_cpu, div, is_map_key))); } return struct_to_str(elems); } case Type::tuple: { std::vector elems; for (auto &field : type.GetFields()) { std::vector elem_data(value.begin() + field.offset, value.begin() + field.offset + field.type.GetSize()); elems.push_back(value_to_str( bpftrace, field.type, elem_data, is_per_cpu, div, false)); } return tuple_to_str(elems, false); } case Type::count_t: { return std::to_string(reduce_value(value, nvalues) / div); } case Type::avg_t: { // on this code path, avg is calculated in the kernel while // printing the entire map is handled in a different function // which shouldn't call this assert(!is_per_cpu); if (type.IsSigned()) { return std::to_string(read_data(value.data()) / div); } return std::to_string(read_data(value.data()) / div); } case Type::integer: { auto sign = type.IsSigned(); switch (type.GetIntBitWidth()) { // clang-format off case 64: if (sign) return std::to_string(reduce_value(value, nvalues) / static_cast(div)); return std::to_string(reduce_value(value, nvalues) / div); case 32: if (sign) return std::to_string( reduce_value(value, nvalues) / static_cast(div)); return std::to_string(reduce_value(value, nvalues) / div); case 16: if (sign) return std::to_string( reduce_value(value, nvalues) / static_cast(div)); return std::to_string(reduce_value(value, nvalues) / div); case 8: if (sign) return std::to_string( reduce_value(value, nvalues) / static_cast(div)); return std::to_string(reduce_value(value, nvalues) / div); // clang-format on default: LOG(BUG) << "value_to_str: Invalid int bitwidth: " << type.GetIntBitWidth() << "provided"; return {}; } } case Type::sum_t: { if (type.IsSigned()) return std::to_string(reduce_value(value, nvalues) / div); return std::to_string(reduce_value(value, nvalues) / div); } case Type::max_t: case Type::min_t: { if (is_per_cpu) { if (type.IsSigned()) { return std::to_string( min_max_value(value, nvalues, type.IsMaxTy()) / div); } return std::to_string( min_max_value(value, nvalues, type.IsMaxTy()) / div); } if (type.IsSigned()) { return std::to_string(read_data(value.data()) / div); } return std::to_string(read_data(value.data()) / div); } case Type::timestamp: { return bpftrace.resolve_timestamp( reinterpret_cast(value.data())->mode, reinterpret_cast(value.data()) ->strftime_id, reinterpret_cast(value.data())->nsecs); } case Type::mac_address: { return bpftrace.resolve_mac_address(value.data()); } case Type::cgroup_path_t: { return bpftrace.resolve_cgroup_path( reinterpret_cast(value.data()) ->cgroup_path_id, reinterpret_cast(value.data()) ->cgroup_id); } case Type::strerror_t: { return strerror(read_data(value.data())); } case Type::none: { return ""; } case Type::voidtype: case Type::hist_t: case Type::lhist_t: case Type::stack_mode: case Type::pointer: case Type::reference: case Type::stats_t: case Type::timestamp_mode: { LOG(BUG) << "Invalid value type: " << type; } } return ""; } std::string Output::map_key_str(BPFtrace &bpftrace, const SizedType &arg, const std::vector &data) const { std::ostringstream ptr; switch (arg.GetTy()) { case Type::integer: case Type::kstack_t: case Type::ustack_t: case Type::timestamp: case Type::ksym_t: case Type::usym_t: case Type::inet: case Type::username: case Type::string: case Type::buffer: case Type::pointer: case Type::array: case Type::mac_address: case Type::record: case Type::count_t: case Type::avg_t: case Type::max_t: case Type::min_t: case Type::sum_t: return value_to_str(bpftrace, arg, data, false, 1, true); case Type::tuple: { std::vector elems; for (auto &field : arg.GetFields()) { std::vector elem_data(data.begin() + field.offset, data.begin() + field.offset + field.type.GetSize()); elems.push_back( value_to_str(bpftrace, field.type, elem_data, false, 1, true)); } return tuple_to_str(elems, true); } case Type::cgroup_path_t: case Type::strerror_t: case Type::hist_t: case Type::lhist_t: case Type::none: case Type::reference: case Type::stack_mode: case Type::stats_t: case Type::timestamp_mode: case Type::voidtype: LOG(BUG) << "Invalid mapkey argument type: " << arg; } return ""; } std::string Output::array_to_str(const std::vector &elems) const { return "[" + str_join(elems, ",") + "]"; } std::string Output::struct_to_str(const std::vector &elems) const { return "{ " + str_join(elems, ", ") + " }"; } void Output::map_contents( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const { uint32_t i = 0; size_t total = values_by_key.size(); const auto &map_type = bpftrace.resources.maps_info.at(map.name()).value_type; bool first = true; for (auto &pair : values_by_key) { auto key = pair.first; auto value = pair.second; if (top) { if (total > top && i++ < (total - top)) continue; } if (first) first = false; else map_elem_delim(map_type); auto key_str = map_key_to_str(bpftrace, map, key); auto value_str = value_to_str( bpftrace, map_type, value, map.is_per_cpu_type(), div); map_key_val(map_type, key_str, value_str); } } void Output::map_hist_contents( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const { uint32_t i = 0; const auto &map_info = bpftrace.resources.maps_info.at(map.name()); const auto &map_type = map_info.value_type; bool first = true; for (auto &key_count : total_counts_by_key) { auto &key = key_count.first; auto &value = values_by_key.at(key); if (top && values_by_key.size() > top && i++ < (values_by_key.size() - top)) continue; if (first) first = false; else map_elem_delim(map_type); auto key_str = map_key_to_str(bpftrace, map, key); std::string val_str; if (map_type.IsHistTy()) { if (!map_info.hist_bits_arg.has_value()) LOG(BUG) << "call to hist with missing \"bits\" argument"; val_str = hist_to_str(value, div, *map_info.hist_bits_arg); } else { auto &args = map_info.lhist_args; if (!args.has_value()) LOG(BUG) << "call to lhist with missing arguments"; val_str = lhist_to_str(value, args->min, args->max, args->step); } map_key_val(map_type, key_str, val_str); } } void Output::map_stats_contents( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const { const auto &map_type = bpftrace.resources.maps_info.at(map.name()).value_type; uint32_t i = 0; size_t total = values_by_key.size(); bool first = true; for (auto &[key, value] : values_by_key) { if (top && map_type.IsAvgTy()) { if (total > top && i++ < (total - top)) continue; } if (first) first = false; else map_elem_delim(map_type); auto key_str = map_key_to_str(bpftrace, map, key); std::string total_str; std::string count_str; std::string avg_str; if (map_type.IsSigned()) { auto stats = stats_value(value, bpftrace.ncpus_); avg_str = std::to_string(stats.avg / div); total_str = std::to_string(stats.total); count_str = std::to_string(stats.count); } else { auto stats = stats_value(value, bpftrace.ncpus_); avg_str = std::to_string(stats.avg / div); total_str = std::to_string(stats.total); count_str = std::to_string(stats.count); } std::string value_str; if (map_type.IsStatsTy()) { std::vector> stats = { { "count", std::move(count_str) }, { "average", std::move(avg_str) }, { "total", std::move(total_str) } }; value_str = key_value_pairs_to_str(stats); } else { value_str = std::move(avg_str); } map_key_val(map_type, key_str, value_str); } } void TextOutput::map( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const { map_contents(bpftrace, map, top, div, values_by_key); out_ << std::endl; } std::string TextOutput::hist_to_str(const std::vector &values, uint32_t div, uint32_t k) const { int min_index, max_index, max_value; hist_prepare(values, min_index, max_index, max_value); if (max_index == -1) return ""; std::ostringstream res; for (int i = min_index; i <= max_index; i++) { std::ostringstream header; // Index 0 is for negative values. Following that, each sequence // of N = 1 << k indexes represents one power of 2. // In particular: // - the first set of N indexes is for values 0..N-1 // (one value per index) // - the second and following sets of N indexes each contain // <1, 2, 4 .. and subsequent powers of 2> values per index. // // Since the first and second set are closed intervals and the value // of each interval equals "index - 1", we print it directly. // Higher indexes contain multiple values and we use helpers to print // the range as open intervals. if (i == 0) { header << "(..., 0)"; } else if (i <= (2 << k)) { header << "[" << (i - 1) << "]"; } else { // Use a helper function to print the interval boundaries. header << "[" << hist_index_label(i - 1, k); header << ", " << hist_index_label(i, k) << ")"; } int max_width = 52; int bar_width = values.at(i) / static_cast(max_value) * max_width; std::string bar(bar_width, '@'); res << std::setw(16) << std::left << header.str() << std::setw(8) << std::right << (values.at(i) / div) << " |" << std::setw(max_width) << std::left << bar << "|" << std::endl; } return res.str(); } std::string TextOutput::lhist_to_str(const std::vector &values, int min, int max, int step) const { int max_index, max_value, buckets, start_value, end_value; lhist_prepare(values, min, max, step, max_index, max_value, buckets, start_value, end_value); if (max_index == -1) return ""; std::ostringstream res; for (int i = start_value; i <= end_value; i++) { int max_width = 52; int bar_width = values.at(i) / static_cast(max_value) * max_width; std::ostringstream header; if (i == 0) { header << "(..., " << lhist_index_label(min, step) << ")"; } else if (i == (buckets + 1)) { header << "[" << lhist_index_label(max, step) << ", ...)"; } else { header << "[" << lhist_index_label((i - 1) * step + min, step); header << ", " << lhist_index_label(i * step + min, step) << ")"; } std::string bar(bar_width, '@'); res << std::setw(16) << std::left << header.str() << std::setw(8) << std::right << values.at(i) << " |" << std::setw(max_width) << std::left << bar << "|" << std::endl; } return res.str(); } void TextOutput::map_hist( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const { map_hist_contents( bpftrace, map, top, div, values_by_key, total_counts_by_key); out_ << std::endl; } void TextOutput::map_stats( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const { map_stats_contents(bpftrace, map, top, div, values_by_key); out_ << std::endl << std::endl; } void TextOutput::value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const { out_ << value_to_str(bpftrace, ty, value, false, 1) << std::endl; } std::string TextOutput::value_to_str(BPFtrace &bpftrace, const SizedType &type, const std::vector &value, bool is_per_cpu, uint32_t div, bool is_map_key) const { switch (type.GetTy()) { case Type::pointer: case Type::reference: { std::ostringstream res; res << "0x" << std::hex << read_data(value.data()); return res.str(); } case Type::integer: { if (type.IsEnumTy() && div == 1) { assert(!is_per_cpu); auto data = value.data(); auto enum_name = type.GetName(); uint64_t enum_val; switch (type.GetIntBitWidth()) { case 64: enum_val = read_data(data); break; case 32: enum_val = read_data(data); break; case 16: enum_val = read_data(data); break; case 8: enum_val = read_data(data); break; default: LOG(BUG) << "value_to_str: Invalid int bitwidth: " << type.GetIntBitWidth() << "provided"; return {}; } if (bpftrace.enum_defs_.contains(enum_name) && bpftrace.enum_defs_[enum_name].contains(enum_val)) { return bpftrace.enum_defs_[enum_name][enum_val]; } else { // Fall back to something comprehensible in case user somehow // tricked the type system into accepting an invalid enum. return std::to_string(enum_val); } } [[fallthrough]]; } default: { return Output::value_to_str( bpftrace, type, value, is_per_cpu, div, is_map_key); } }; } void TextOutput::message(MessageType type __attribute__((unused)), const std::string &msg, bool nl) const { out_ << msg; if (nl) out_ << std::endl; } void TextOutput::lost_events(uint64_t lost) const { out_ << "Lost " << lost << " events" << std::endl; } void TextOutput::attached_probes(uint64_t num_probes) const { if (num_probes == 1) out_ << "Attaching " << num_probes << " probe..." << std::endl; else out_ << "Attaching " << num_probes << " probes..." << std::endl; } void TextOutput::helper_error(int func_id, int retcode, const location &loc) const { LOG(WARNING, loc, out_) << get_helper_error_msg(func_id, retcode) << "\nAdditional Info - helper: " << libbpf::bpf_func_name[func_id] << ", retcode: " << retcode; } std::string TextOutput::field_to_str(const std::string &name, const std::string &value) const { return "." + name + " = " + value; } std::string TextOutput::tuple_to_str(const std::vector &elems, bool is_map_key) const { if (!is_map_key) { return "(" + str_join(elems, ", ") + ")"; } return str_join(elems, ", "); } std::string TextOutput::map_key_to_str(BPFtrace &bpftrace, const BpfMap &map, const std::vector &key) const { const auto &key_type = bpftrace.resources.maps_info.at(map.name()).key_type; if (key_type.IsNoneTy()) return map.name(); return map.name() + "[" + map_key_str(bpftrace, key_type, key) + "]"; } void TextOutput::map_key_val(const SizedType &map_type, const std::string &key, const std::string &val) const { out_ << key; if (map_type.IsHistTy() || map_type.IsLhistTy()) out_ << ":\n"; else out_ << ": "; out_ << val; } void TextOutput::map_elem_delim(const SizedType &map_type) const { if (!map_type.IsKstackTy() && !map_type.IsUstackTy() && !map_type.IsKsymTy() && !map_type.IsUsymTy() && !map_type.IsInetTy()) out_ << "\n"; } std::string TextOutput::key_value_pairs_to_str( std::vector> &keyvals) const { std::vector elems; for (auto &e : keyvals) elems.push_back(e.first + " " + e.second); return str_join(elems, ", "); } std::string JsonOutput::json_escape(const std::string &str) const { std::ostringstream escaped; for (const char &c : str) { switch (c) { case '"': escaped << "\\\""; break; case '\\': escaped << "\\\\"; break; case '\n': escaped << "\\n"; break; case '\r': escaped << "\\r"; break; case '\t': escaped << "\\t"; break; default: // c always >= '\x00' if (c <= '\x1f') { escaped << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(c); } else { escaped << c; } } } return escaped.str(); } void JsonOutput::map( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const { if (values_by_key.empty()) return; const auto &map_key = bpftrace.resources.maps_info.at(map.name()).key_type; out_ << R"({"type": ")" << MessageType::map << R"(", "data": {)"; out_ << "\"" << json_escape(map.name()) << "\": "; if (!map_key.IsNoneTy()) // check if this map has keys out_ << "{"; map_contents(bpftrace, map, top, div, values_by_key); if (!map_key.IsNoneTy()) out_ << "}"; out_ << "}}" << std::endl; } std::string JsonOutput::hist_to_str(const std::vector &values, uint32_t div, uint32_t k) const { int min_index, max_index, max_value; hist_prepare(values, min_index, max_index, max_value); if (max_index == -1) return "[]"; std::ostringstream res; res << "["; for (int i = min_index; i <= max_index; i++) { if (i > min_index) res << ", "; res << "{"; // See description in TextOutput::hist_to_str(): // first index is for negative values, the next 2 sets of // N = 2^k indexes have one value each (equal to i -1) // and remaining sets of N indexes each cover one power of 2, // whose ranges are computed as described in hist_index_label() if (i == 0) { res << "\"max\": -1, "; } else if (i <= (2 << k)) { res << "\"min\": " << i - 1 << ", \"max\": " << i - 1 << ", "; } else { const uint32_t n = 1 << k; uint32_t power = ((i - 1) >> k) - 1, bucket = (i - 1) & (n - 1); const long low = (1ULL << power) * (n + bucket); power = (i >> k) - 1; bucket = i & (n - 1); const long high = (1ULL << power) * (n + bucket) - 1; res << "\"min\": " << low << ", \"max\": " << high << ", "; } res << "\"count\": " << values.at(i) / div; res << "}"; } res << "]"; return res.str(); } std::string JsonOutput::lhist_to_str(const std::vector &values, int min, int max, int step) const { int max_index, max_value, buckets, start_value, end_value; lhist_prepare(values, min, max, step, max_index, max_value, buckets, start_value, end_value); if (max_index == -1) return "[]"; std::ostringstream res; res << "["; for (int i = start_value; i <= end_value; i++) { if (i > start_value) res << ", "; res << "{"; if (i == 0) { res << "\"max\": " << min - 1 << ", "; } else if (i == (buckets + 1)) { res << "\"min\": " << max << ", "; } else { long low = (i - 1) * step + min; long high = i * step + min - 1; res << "\"min\": " << low << ", \"max\": " << high << ", "; } res << "\"count\": " << values.at(i); res << "}"; } res << "]"; return res.str(); } void JsonOutput::map_hist( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const { if (total_counts_by_key.empty()) return; const auto &map_key = bpftrace.resources.maps_info.at(map.name()).key_type; out_ << R"({"type": ")" << MessageType::hist << R"(", "data": {)"; out_ << "\"" << json_escape(map.name()) << "\": "; if (!map_key.IsNoneTy()) // check if this map has keys out_ << "{"; map_hist_contents( bpftrace, map, top, div, values_by_key, total_counts_by_key); if (!map_key.IsNoneTy()) out_ << "}"; out_ << "}}" << std::endl; } void JsonOutput::map_stats( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const { if (values_by_key.empty()) return; const auto &map_key = bpftrace.resources.maps_info.at(map.name()).key_type; out_ << R"({"type": ")" << MessageType::stats << R"(", "data": {)"; out_ << "\"" << json_escape(map.name()) << "\": "; if (!map_key.IsNoneTy()) // check if this map has keys out_ << "{"; map_stats_contents(bpftrace, map, top, div, values_by_key); if (!map_key.IsNoneTy()) out_ << "}"; out_ << "}}" << std::endl; } void JsonOutput::value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const { out_ << R"({"type": ")" << MessageType::value << R"(", "data": )" << value_to_str(bpftrace, ty, value, false, 1, false) << "}" << std::endl; } void JsonOutput::message(MessageType type, const std::string &msg, bool nl __attribute__((unused))) const { out_ << R"({"type": ")" << type << R"(", "data": ")" << json_escape(msg) << "\"}" << std::endl; } void JsonOutput::message(MessageType type, const std::string &field, uint64_t value) const { out_ << R"({"type": ")" << type << R"(", "data": )" << "{\"" << field << "\": " << value << "}" << "}" << std::endl; } void JsonOutput::lost_events(uint64_t lost) const { message(MessageType::lost_events, "events", lost); } void JsonOutput::attached_probes(uint64_t num_probes) const { message(MessageType::attached_probes, "probes", num_probes); } void JsonOutput::helper_error(int func_id, int retcode, const location &loc) const { out_ << R"({"type": "helper_error", "msg": ")" << get_helper_error_msg(func_id, retcode) << R"(", "helper": ")" << libbpf::bpf_func_name[func_id] << R"(", "retcode": )" << retcode << ", \"line\": " << loc.begin.line << ", \"col\": " << loc.begin.column << "}" << std::endl; } std::string JsonOutput::field_to_str(const std::string &name, const std::string &value) const { return "\"" + name + "\": " + value; } std::string JsonOutput::tuple_to_str(const std::vector &elems, bool is_map_key) const { if (!is_map_key) { return "[" + str_join(elems, ",") + "]"; } return str_join(elems, ","); } std::string JsonOutput::value_to_str(BPFtrace &bpftrace, const SizedType &type, const std::vector &value, bool is_per_cpu, uint32_t div, bool is_map_key) const { std::string str; switch (type.GetTy()) { case Type::pointer: case Type::reference: str = std::to_string(read_data(value.data())); break; default: str = Output::value_to_str( bpftrace, type, value, is_per_cpu, div, is_map_key); }; if (is_quoted_type(type)) { if (is_map_key) { return json_escape(str); } else { return "\"" + json_escape(str) + "\""; } } return str; } std::string JsonOutput::map_key_to_str(BPFtrace &bpftrace, const BpfMap &map, const std::vector &key) const { const auto &key_type = bpftrace.resources.maps_info.at(map.name()).key_type; if (key_type.IsNoneTy()) { return ""; } return "\"" + json_escape(map_key_str(bpftrace, key_type, key)) + "\""; } void JsonOutput::map_key_val(const SizedType &map_type __attribute__((unused)), const std::string &key, const std::string &val) const { out_ << (key.empty() ? val : key + ": " + val); } void JsonOutput::map_elem_delim(const SizedType &map __attribute__((unused))) const { out_ << ", "; } std::string JsonOutput::key_value_pairs_to_str( std::vector> &keyvals) const { std::vector elems; for (auto &e : keyvals) elems.push_back("\"" + e.first + "\": " + e.second); return "{" + str_join(elems, ", ") + "}"; } } // namespace bpftrace bpftrace-0.23.2/src/output.h000066400000000000000000000325171477746507000157150ustar00rootroot00000000000000#pragma once #include #include #include #include #include "bpfmap.h" #include "location.hh" #include "types.h" namespace bpftrace { class BPFtrace; enum class MessageType { // don't forget to update std::ostream& operator<<(std::ostream& out, // MessageType type) in output.cpp map, value, hist, stats, printf, time, cat, join, syscall, attached_probes, lost_events, helper_error, }; std::ostream &operator<<(std::ostream &out, MessageType type); // Abstract class (interface) for output // Provides default implementation of some methods for formatting map and // non-map values into strings. // All output formatters should extend this class and override (at least) pure // virtual methods. class Output { public: explicit Output(std::ostream &out = std::cout, std::ostream &err = std::cerr) : out_(out), err_(err) { } Output(const Output &) = delete; Output &operator=(const Output &) = delete; virtual ~Output() = default; virtual std::ostream &outputstream() const { return out_; }; // Write map to output virtual void map( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const = 0; // Write map histogram to output virtual void map_hist( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const = 0; // Write map statistics to output virtual void map_stats( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const = 0; // Write non-map value to output // Ideally, the implementation should use value_to_str to convert a value into // a string, format it properly, and print it to out_. virtual void value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const = 0; virtual void message(MessageType type, const std::string &msg, bool nl = true) const = 0; virtual void lost_events(uint64_t lost) const = 0; virtual void attached_probes(uint64_t num_probes) const = 0; virtual void helper_error(int func_id, int retcode, const location &loc) const = 0; protected: std::ostream &out_; std::ostream &err_; void hist_prepare(const std::vector &values, int &min_index, int &max_index, int &max_value) const; void lhist_prepare(const std::vector &values, int min, int max, int step, int &max_index, int &max_value, int &buckets, int &start_value, int &end_value) const; std::string get_helper_error_msg(int func_id, int retcode) const; // Convert a log2 histogram into string virtual std::string hist_to_str(const std::vector &values, uint32_t div, uint32_t k) const = 0; // Convert a linear histogram into string virtual std::string lhist_to_str(const std::vector &values, int min, int max, int step) const = 0; // Convert map into string // Default behaviour: format each (key, value) pair using output-specific // methods and join them into a single string virtual void map_contents( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const; // Convert map histogram into string // Default behaviour: format each (key, hist) pair using output-specific // methods and join them into a single string virtual void map_hist_contents( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const; // Convert map statistics into string // Default behaviour: format each (key, stats) pair using output-specific // methods and join them into a single string virtual void map_stats_contents( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const; // Convert map key to string virtual std::string map_key_to_str(BPFtrace &bpftrace, const BpfMap &map, const std::vector &key) const = 0; // Properly join map key and value strings virtual void map_key_val(const SizedType &map_type, const std::string &key, const std::string &val) const = 0; // Delimiter to join (properly formatted) map elements into a single string virtual void map_elem_delim(const SizedType &map_type) const = 0; // Convert non-map value into string // This method should properly handle all non-map value types. // Aggregate types (array, struct, tuple) are formatted using output-specific // methods. virtual std::string value_to_str(BPFtrace &bpftrace, const SizedType &type, const std::vector &value, bool is_per_cpu = false, uint32_t div = 1, bool is_map_key = false) const; virtual std::string map_key_str(BPFtrace &bpftrace, const SizedType &arg, const std::vector &data) const; // Convert an array to string // Default behaviour: [elem1, elem2, ...] virtual std::string array_to_str(const std::vector &elems) const; // Convert a struct to string // Default behaviour: { elem1, elem2, ... } // elems are expected to be properly formatted virtual std::string struct_to_str( const std::vector &elems) const; // Convert struct field (given by its name and value) into string virtual std::string field_to_str(const std::string &name, const std::string &value) const = 0; // Convert tuple to string virtual std::string tuple_to_str(const std::vector &elems, bool is_map_key = false) const = 0; // Convert a vector of (key, value) pairs into string // Used for properly formatting map statistics virtual std::string key_value_pairs_to_str( std::vector> &keyvals) const = 0; }; class TextOutput : public Output { public: explicit TextOutput(std::ostream &out = std::cout, std::ostream &err = std::cerr) : Output(out, err) { } void map( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const override; void map_hist(BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const override; void map_stats( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const override; virtual void value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const override; void message(MessageType type, const std::string &msg, bool nl = true) const override; void lost_events(uint64_t lost) const override; void attached_probes(uint64_t num_probes) const override; void helper_error(int func_id, int retcode, const location &loc) const override; protected: std::string value_to_str(BPFtrace &bpftrace, const SizedType &type, const std::vector &value, bool is_per_cpu, uint32_t div, bool is_map_key = false) const override; static std::string hist_index_label(uint32_t index, uint32_t bits); static std::string lhist_index_label(int number, int step); virtual std::string hist_to_str(const std::vector &values, uint32_t div, uint32_t k) const override; virtual std::string lhist_to_str(const std::vector &values, int min, int max, int step) const override; std::string map_key_to_str(BPFtrace &bpftrace, const BpfMap &map, const std::vector &key) const override; void map_key_val(const SizedType &map_type, const std::string &key, const std::string &val) const override; void map_elem_delim(const SizedType &map_type) const override; std::string field_to_str(const std::string &name, const std::string &value) const override; std::string tuple_to_str(const std::vector &elems, bool is_map_key) const override; std::string key_value_pairs_to_str( std::vector> &keyvals) const override; }; class JsonOutput : public Output { public: explicit JsonOutput(std::ostream &out = std::cout, std::ostream &err = std::cerr) : Output(out, err) { } void map( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const override; void map_hist(BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::map, std::vector> &values_by_key, const std::vector, uint64_t>> &total_counts_by_key) const override; void map_stats( BPFtrace &bpftrace, const BpfMap &map, uint32_t top, uint32_t div, const std::vector, std::vector>> &values_by_key) const override; virtual void value(BPFtrace &bpftrace, const SizedType &ty, std::vector &value) const override; void message(MessageType type, const std::string &msg, bool nl = true) const override; void message(MessageType type, const std::string &field, uint64_t value) const; void lost_events(uint64_t lost) const override; void attached_probes(uint64_t num_probes) const override; void helper_error(int func_id, int retcode, const location &loc) const override; private: std::string json_escape(const std::string &str) const; protected: std::string value_to_str(BPFtrace &bpftrace, const SizedType &type, const std::vector &value, bool is_per_cpu, uint32_t div, bool is_map_key) const override; std::string hist_to_str(const std::vector &values, uint32_t div, uint32_t k) const override; std::string lhist_to_str(const std::vector &values, int min, int max, int step) const override; std::string map_key_to_str(BPFtrace &bpftrace, const BpfMap &map, const std::vector &key) const override; void map_key_val(const SizedType &map_type, const std::string &key, const std::string &val) const override; void map_elem_delim(const SizedType &map_type) const override; std::string field_to_str(const std::string &name, const std::string &value) const override; std::string tuple_to_str(const std::vector &elems, bool is_map_key) const override; std::string key_value_pairs_to_str( std::vector> &keyvals) const override; }; } // namespace bpftrace bpftrace-0.23.2/src/parser.yy000066400000000000000000000730371477746507000160650ustar00rootroot00000000000000%skeleton "lalr1.cc" %require "3.0.4" %defines %define api.namespace { bpftrace } // Pretend like the following %define is uncommented. We set the actual // definition from cmake to handle older versions of bison. // %define api.parser.class { Parser } %define api.token.constructor %define api.value.type variant %define define_location_comparison %define parse.assert %define parse.trace %expect 5 %define parse.error verbose %param { bpftrace::Driver &driver } %param { void *yyscanner } %locations // Forward declarations of classes referenced in the parser %code requires { #include #include #include namespace bpftrace { class Driver; namespace ast { class Node; } // namespace ast } // namespace bpftrace #include "ast/ast.h" } %{ #include #include "driver.h" #include "lexer.h" void yyerror(bpftrace::Driver &driver, const char *s); %} %token END 0 "end of file" COLON ":" SEMI ";" LBRACE "{" RBRACE "}" LBRACKET "[" RBRACKET "]" LPAREN "(" RPAREN ")" QUES "?" ENDPRED "end predicate" COMMA "," PARAMCOUNT "$#" ASSIGN "=" EQ "==" NE "!=" LE "<=" GE ">=" LEFT "<<" RIGHT ">>" LT "<" GT ">" LAND "&&" LOR "||" PLUS "+" INCREMENT "++" LEFTASSIGN "<<=" RIGHTASSIGN ">>=" PLUSASSIGN "+=" MINUSASSIGN "-=" MULASSIGN "*=" DIVASSIGN "/=" MODASSIGN "%=" BANDASSIGN "&=" BORASSIGN "|=" BXORASSIGN "^=" MINUS "-" DECREMENT "--" MUL "*" DIV "/" MOD "%" BAND "&" BOR "|" BXOR "^" LNOT "!" BNOT "~" DOT "." PTR "->" STRUCT "struct" UNION "union" ; %token BUILTIN "builtin" %token CALL "call" %token CALL_BUILTIN "call_builtin" %token INT_TYPE "integer type" %token BUILTIN_TYPE "builtin type" %token SUBPROG "subprog" %token SIZED_TYPE "sized type" %token IDENT "identifier" %token PATH "path" %token CPREPROC "preprocessor directive" %token STRUCT_DEFN "struct definition" %token ENUM "enum" %token STRING "string" %token MAP "map" %token VAR "variable" %token PARAM "positional parameter" %token INT "integer" %token STACK_MODE "stack_mode" %token CONFIG "config" %token UNROLL "unroll" %token WHILE "while" %token FOR "for" %token RETURN "return" %token IF "if" %token ELSE "else" %token CONTINUE "continue" %token BREAK "break" %token SIZEOF "sizeof" %token OFFSETOF "offsetof" %token LET "let" %type unary_op compound_op %type attach_point_def c_definitions ident keyword external_name %type > struct_field %type attach_point %type attach_points %type call %type sizeof_expr %type offsetof_expr %type and_expr addi_expr primary_expr cast_expr conditional_expr equality_expr expr logical_and_expr muli_expr %type logical_or_expr map_or_var or_expr postfix_expr relational_expr shift_expr tuple_access_expr unary_expr xor_expr %type vargs %type subprog %type subprog_arg %type subprog_args %type int %type map %type param %type pred %type probe %type > probes_and_subprogs %type config %type assign_stmt block_stmt expr_stmt if_stmt jump_stmt loop_stmt config_assign_stmt for_stmt %type var_decl_stmt %type block block_or_if stmt_list config_block config_assign_stmt_list %type type int_type pointer_type struct_type %type var %left COMMA %right ASSIGN LEFTASSIGN RIGHTASSIGN PLUSASSIGN MINUSASSIGN MULASSIGN DIVASSIGN MODASSIGN BANDASSIGN BORASSIGN BXORASSIGN %left QUES COLON %left LOR %left LAND %left BOR %left BXOR %left BAND %left EQ NE %left LE GE LT GT %left LEFT RIGHT %left PLUS MINUS %left MUL DIV MOD %right LNOT BNOT %left LPAREN RPAREN LBRACKET RBRACKET DOT PTR %start program %% program: c_definitions config probes_and_subprogs END { driver.ctx.root = driver.ctx.make_node($1, $2, std::move($3.second), std::move($3.first), @$); } ; c_definitions: CPREPROC c_definitions { $$ = $1 + "\n" + $2; } | STRUCT STRUCT_DEFN c_definitions { $$ = $2 + ";\n" + $3; } | STRUCT ENUM c_definitions { $$ = $2 + ";\n" + $3; } | %empty { $$ = std::string(); } ; type: int_type { $$ = $1; } | BUILTIN_TYPE { static std::unordered_map type_map = { {"void", CreateVoid()}, {"min_t", CreateMin(true)}, {"max_t", CreateMax(true)}, {"sum_t", CreateSum(true)}, {"count_t", CreateCount(true)}, {"avg_t", CreateAvg(true)}, {"stats_t", CreateStats(true)}, {"umin_t", CreateMin(false)}, {"umax_t", CreateMax(false)}, {"usum_t", CreateSum(false)}, {"ucount_t", CreateCount(false)}, {"uavg_t", CreateAvg(false)}, {"ustats_t", CreateStats(false)}, {"timestamp", CreateTimestamp()}, {"macaddr_t", CreateMacAddress()}, {"cgroup_path_t", CreateCgroupPath()}, {"strerror_t", CreateStrerror()}, }; $$ = type_map[$1]; } | SIZED_TYPE { if ($1 == "string") { $$ = CreateString(0); } else if ($1 == "inet") { $$ = CreateInet(0); } else if ($1 == "buffer") { $$ = CreateBuffer(0); } } | SIZED_TYPE "[" INT "]" { if ($1 == "string") { $$ = CreateString($3); } else if ($1 == "inet") { $$ = CreateInet($3); } else if ($1 == "buffer") { $$ = CreateBuffer($3); } } | int_type "[" INT "]" { $$ = CreateArray($3, $1); } | struct_type "[" INT "]" { $$ = CreateArray($3, $1); } | int_type "[" "]" { $$ = CreateArray(0, $1); } | pointer_type { $$ = $1; } | struct_type { $$ = $1; } ; int_type: INT_TYPE { static std::unordered_map type_map = { {"bool", CreateBool()}, {"uint8", CreateUInt(8)}, {"uint16", CreateUInt(16)}, {"uint32", CreateUInt(32)}, {"uint64", CreateUInt(64)}, {"int8", CreateInt(8)}, {"int16", CreateInt(16)}, {"int32", CreateInt(32)}, {"int64", CreateInt(64)}, }; $$ = type_map[$1]; } ; pointer_type: type "*" { $$ = CreatePointer($1); } ; struct_type: STRUCT IDENT { $$ = ast::ident_to_sized_type($2); } ; config: CONFIG ASSIGN config_block { $$ = driver.ctx.make_node(std::move($3), @$); } | %empty { $$ = nullptr; } ; /* * The last statement in a config_block does not require a trailing semicolon. */ config_block: "{" config_assign_stmt_list "}" { $$ = std::move($2); } | "{" config_assign_stmt_list config_assign_stmt "}" { $$ = std::move($2); $$.push_back($3); } ; config_assign_stmt_list: config_assign_stmt_list config_assign_stmt ";" { $$ = std::move($1); $$.push_back($2); } | %empty { $$ = ast::StatementList{}; } ; config_assign_stmt: IDENT ASSIGN expr { $$ = driver.ctx.make_node($1, $3, @2); } ; subprog: SUBPROG IDENT "(" subprog_args ")" ":" type block { $$ = driver.ctx.make_node($2, $7, std::move($4), std::move($8), @$); } | SUBPROG IDENT "(" ")" ":" type block { $$ = driver.ctx.make_node($2, $6, ast::SubprogArgList(), std::move($7), @$); } ; subprog_args: subprog_args "," subprog_arg { $$ = std::move($1); $$.push_back($3); } | subprog_arg { $$ = ast::SubprogArgList{$1}; } ; subprog_arg: VAR ":" type { $$ = driver.ctx.make_node($1, $3, @$); } ; probes_and_subprogs: probes_and_subprogs probe { $$ = std::move($1); $$.first.push_back($2); } | probes_and_subprogs subprog { $$ = std::move($1); $$.second.push_back($2); } | probe { $$ = { ast::ProbeList{$1}, ast::SubprogList{}}; } | subprog { $$ = { ast::ProbeList{}, ast::SubprogList{$1}}; } ; probe: attach_points pred block { if (!driver.listing_) $$ = driver.ctx.make_node(std::move($1), $2, driver.ctx.make_node(std::move($3), @3), @$); else { error(@$, "unexpected listing query format"); YYERROR; } } | attach_points END { if (driver.listing_) $$ = driver.ctx.make_node(std::move($1), nullptr, driver.ctx.make_node(ast::StatementList(), @$), @$); else { error(@$, "unexpected end of file, expected {"); YYERROR; } } ; attach_points: attach_points "," attach_point { $$ = std::move($1); $$.push_back($3); } | attach_point { $$ = ast::AttachPointList{$1}; } ; attach_point: attach_point_def { $$ = driver.ctx.make_node($1, false, @$); } ; attach_point_def: attach_point_def ident { $$ = $1 + $2; } // Since we're double quoting the STRING for the benefit of the // AttachPointParser, we have to make sure we re-escape any double // quotes. | attach_point_def STRING { $$ = $1 + "\"" + std::regex_replace($2, std::regex("\""), "\\\"") + "\""; } | attach_point_def PATH { $$ = $1 + $2; } | attach_point_def INT { $$ = $1 + std::to_string($2); } | attach_point_def COLON { $$ = $1 + ":"; } | attach_point_def DOT { $$ = $1 + "."; } | attach_point_def PLUS { $$ = $1 + "+"; } | attach_point_def MUL { $$ = $1 + "*"; } | attach_point_def LBRACKET { $$ = $1 + "["; } | attach_point_def RBRACKET { $$ = $1 + "]"; } | attach_point_def param { if ($2->ptype != PositionalParameterType::positional) { error(@$, "Not a positional parameter"); YYERROR; } // "Un-parse" the positional parameter back into text so // we can give it to the AttachPointParser. This is kind of // a hack but there doesn't look to be any other way. $$ = $1 + "$" + std::to_string($2->n); } | %empty { $$ = ""; } ; pred: DIV expr ENDPRED { $$ = driver.ctx.make_node($2, @$); } | %empty { $$ = nullptr; } ; param: PARAM { try { long n = std::stol($1.substr(1, $1.size()-1)); if (n == 0) throw std::exception(); $$ = driver.ctx.make_node(PositionalParameterType::positional, n, @$); } catch (std::exception const& e) { error(@1, "param " + $1 + " is out of integer range [1, " + std::to_string(std::numeric_limits::max()) + "]"); YYERROR; } } | PARAMCOUNT { $$ = driver.ctx.make_node(PositionalParameterType::count, 0, @$); } ; /* * The last statement in a block does not require a trailing semicolon. */ block: "{" stmt_list "}" { $$ = std::move($2); } | "{" stmt_list expr_stmt "}" { $$ = std::move($2); $$.push_back($3); } ; stmt_list: stmt_list expr_stmt ";" { $$ = std::move($1); $$.push_back($2); } | stmt_list block_stmt { $$ = std::move($1); $$.push_back($2); } | stmt_list var_decl_stmt ";" { $$ = std::move($1); $$.push_back($2); } | %empty { $$ = ast::StatementList{}; } ; block_stmt: loop_stmt { $$ = $1; } | if_stmt { $$ = $1; } | for_stmt { $$ = $1; } ; expr_stmt: expr { $$ = driver.ctx.make_node($1, @1); } | jump_stmt { $$ = $1; } /* * quirk. Assignment is not an expression but the AssignMapStatement makes it difficult * this avoids a r/r conflict */ | assign_stmt { $$ = $1; } ; jump_stmt: BREAK { $$ = driver.ctx.make_node(ast::JumpType::BREAK, @$); } | CONTINUE { $$ = driver.ctx.make_node(ast::JumpType::CONTINUE, @$); } | RETURN { $$ = driver.ctx.make_node(ast::JumpType::RETURN, @$); } | RETURN expr { $$ = driver.ctx.make_node(ast::JumpType::RETURN, $2, @$); } ; loop_stmt: UNROLL "(" int ")" block { $$ = driver.ctx.make_node($3, driver.ctx.make_node(std::move($5), @5), @1 + @4); } | UNROLL "(" param ")" block { $$ = driver.ctx.make_node($3, driver.ctx.make_node(std::move($5), @5), @1 + @4); } | WHILE "(" expr ")" block { $$ = driver.ctx.make_node($3, driver.ctx.make_node(std::move($5), @5), @1); } ; for_stmt: FOR "(" var ":" expr ")" block { $$ = driver.ctx.make_node($3, $5, std::move($7), @1); } ; if_stmt: IF "(" expr ")" block { $$ = driver.ctx.make_node($3, driver.ctx.make_node(std::move($5), @5), driver.ctx.make_node(ast::StatementList(), @1), @$); } | IF "(" expr ")" block ELSE block_or_if { $$ = driver.ctx.make_node($3, driver.ctx.make_node(std::move($5), @5), driver.ctx.make_node(std::move($7), @7), @$); } ; block_or_if: block { $$ = std::move($1); } | if_stmt { $$ = ast::StatementList{$1}; } ; assign_stmt: tuple_access_expr ASSIGN expr { error(@1 + @3, "Tuples are immutable once created. Consider creating a new tuple and assigning it instead."); YYERROR; } | map ASSIGN expr { $$ = driver.ctx.make_node($1, $3, @$); } | var_decl_stmt ASSIGN expr { $$ = driver.ctx.make_node($1, $3, @$); } | var ASSIGN expr { $$ = driver.ctx.make_node($1, $3, @$); } | map compound_op expr { auto b = driver.ctx.make_node($1, $2, $3, @2); $$ = driver.ctx.make_node($1, b, @$); } | var compound_op expr { auto b = driver.ctx.make_node($1, $2, $3, @2); $$ = driver.ctx.make_node($1, b, @$); } ; var_decl_stmt: LET var { $$ = driver.ctx.make_node($2, @$); } | LET var COLON type { $$ = driver.ctx.make_node($2, $4, @$); } ; primary_expr: IDENT { $$ = driver.ctx.make_node($1, @$); } | int { $$ = $1; } | STRING { $$ = driver.ctx.make_node($1, @$); } | STACK_MODE { $$ = driver.ctx.make_node($1, @$); } | BUILTIN { $$ = driver.ctx.make_node($1, @$); } | CALL_BUILTIN { $$ = driver.ctx.make_node($1, @$); } | LPAREN expr RPAREN { $$ = $2; } | param { $$ = $1; } | map_or_var { $$ = $1; } | "(" vargs "," expr ")" { auto &args = $2; args.push_back($4); $$ = driver.ctx.make_node(std::move(args), @$); } ; postfix_expr: primary_expr { $$ = $1; } /* pointer */ | postfix_expr DOT external_name { $$ = driver.ctx.make_node($1, $3, @2); } | postfix_expr PTR external_name { $$ = driver.ctx.make_node(driver.ctx.make_node(ast::Operator::MUL, $1, false, @2), $3, @$); } /* tuple */ | tuple_access_expr { $$ = $1; } /* array */ | postfix_expr "[" expr "]" { $$ = driver.ctx.make_node($1, $3, @2 + @4); } | call { $$ = $1; } | sizeof_expr { $$ = $1; } | offsetof_expr { $$ = $1; } | map_or_var INCREMENT { $$ = driver.ctx.make_node(ast::Operator::INCREMENT, $1, true, @2); } | map_or_var DECREMENT { $$ = driver.ctx.make_node(ast::Operator::DECREMENT, $1, true, @2); } /* errors */ | INCREMENT ident { error(@1, "The ++ operator must be applied to a map or variable"); YYERROR; } | DECREMENT ident { error(@1, "The -- operator must be applied to a map or variable"); YYERROR; } ; /* Tuple factored out so we can use it in the tuple field assignment error */ tuple_access_expr: postfix_expr DOT INT { $$ = driver.ctx.make_node($1, $3, @3); } ; unary_expr: unary_op cast_expr { $$ = driver.ctx.make_node($1, $2, false, @1); } | postfix_expr { $$ = $1; } | INCREMENT map_or_var { $$ = driver.ctx.make_node(ast::Operator::INCREMENT, $2, false, @1); } | DECREMENT map_or_var { $$ = driver.ctx.make_node(ast::Operator::DECREMENT, $2, false, @1); } /* errors */ | ident DECREMENT { error(@1, "The -- operator must be applied to a map or variable"); YYERROR; } | ident INCREMENT { error(@1, "The ++ operator must be applied to a map or variable"); YYERROR; } ; unary_op: MUL { $$ = ast::Operator::MUL; } | BNOT { $$ = ast::Operator::BNOT; } | LNOT { $$ = ast::Operator::LNOT; } | MINUS { $$ = ast::Operator::MINUS; } ; expr: conditional_expr { $$ = $1; } ; conditional_expr: logical_or_expr { $$ = $1; } | logical_or_expr QUES expr COLON conditional_expr { $$ = driver.ctx.make_node($1, $3, $5, @$); } ; logical_or_expr: logical_and_expr { $$ = $1; } | logical_or_expr LOR logical_and_expr { $$ = driver.ctx.make_node($1, ast::Operator::LOR, $3, @2); } ; logical_and_expr: or_expr { $$ = $1; } | logical_and_expr LAND or_expr { $$ = driver.ctx.make_node($1, ast::Operator::LAND, $3, @2); } ; or_expr: xor_expr { $$ = $1; } | or_expr BOR xor_expr { $$ = driver.ctx.make_node($1, ast::Operator::BOR, $3, @2); } ; xor_expr: and_expr { $$ = $1; } | xor_expr BXOR and_expr { $$ = driver.ctx.make_node($1, ast::Operator::BXOR, $3, @2); } ; and_expr: equality_expr { $$ = $1; } | and_expr BAND equality_expr { $$ = driver.ctx.make_node($1, ast::Operator::BAND, $3, @2); } ; equality_expr: relational_expr { $$ = $1; } | equality_expr EQ relational_expr { $$ = driver.ctx.make_node($1, ast::Operator::EQ, $3, @2); } | equality_expr NE relational_expr { $$ = driver.ctx.make_node($1, ast::Operator::NE, $3, @2); } ; relational_expr: shift_expr { $$ = $1; } | relational_expr LE shift_expr { $$ = driver.ctx.make_node($1, ast::Operator::LE, $3, @2); } | relational_expr GE shift_expr { $$ = driver.ctx.make_node($1, ast::Operator::GE, $3, @2); } | relational_expr LT shift_expr { $$ = driver.ctx.make_node($1, ast::Operator::LT, $3, @2); } | relational_expr GT shift_expr { $$ = driver.ctx.make_node($1, ast::Operator::GT, $3, @2); } ; shift_expr: addi_expr { $$ = $1; } | shift_expr LEFT addi_expr { $$ = driver.ctx.make_node($1, ast::Operator::LEFT, $3, @2); } | shift_expr RIGHT addi_expr { $$ = driver.ctx.make_node($1, ast::Operator::RIGHT, $3, @2); } ; muli_expr: cast_expr { $$ = $1; } | muli_expr MUL cast_expr { $$ = driver.ctx.make_node($1, ast::Operator::MUL, $3, @2); } | muli_expr DIV cast_expr { $$ = driver.ctx.make_node($1, ast::Operator::DIV, $3, @2); } | muli_expr MOD cast_expr { $$ = driver.ctx.make_node($1, ast::Operator::MOD, $3, @2); } ; addi_expr: muli_expr { $$ = $1; } | addi_expr PLUS muli_expr { $$ = driver.ctx.make_node($1, ast::Operator::PLUS, $3, @2); } | addi_expr MINUS muli_expr { $$ = driver.ctx.make_node($1, ast::Operator::MINUS, $3, @2); } ; cast_expr: unary_expr { $$ = $1; } | LPAREN type RPAREN cast_expr { $$ = driver.ctx.make_node($2, $4, @1 + @3); } /* workaround for typedef types, see https://github.com/bpftrace/bpftrace/pull/2560#issuecomment-1521783935 */ | LPAREN IDENT RPAREN cast_expr { $$ = driver.ctx.make_node(ast::ident_to_record($2, 0), $4, @1 + @3); } | LPAREN IDENT "*" RPAREN cast_expr { $$ = driver.ctx.make_node(ast::ident_to_record($2, 1), $5, @1 + @4); } | LPAREN IDENT "*" "*" RPAREN cast_expr { $$ = driver.ctx.make_node(ast::ident_to_record($2, 2), $6, @1 + @5); } ; sizeof_expr: SIZEOF "(" type ")" { $$ = driver.ctx.make_node($3, @$); } | SIZEOF "(" expr ")" { $$ = driver.ctx.make_node($3, @$); } ; offsetof_expr: OFFSETOF "(" struct_type "," struct_field ")" { $$ = driver.ctx.make_node($3, $5, @$); } /* For example: offsetof(*curtask, comm) */ | OFFSETOF "(" expr "," struct_field ")" { $$ = driver.ctx.make_node($3, $5, @$); } ; int: MINUS INT { $$ = driver.ctx.make_node((int64_t)(~(uint64_t)($2) + 1), @$, true); } | INT { $$ = driver.ctx.make_node($1, @$, false); } ; keyword: BREAK { $$ = $1; } | CONFIG { $$ = $1; } | CONTINUE { $$ = $1; } | ELSE { $$ = $1; } | FOR { $$ = $1; } | IF { $$ = $1; } | LET { $$ = $1; } | OFFSETOF { $$ = $1; } | RETURN { $$ = $1; } | SIZEOF { $$ = $1; } | UNROLL { $$ = $1; } | WHILE { $$ = $1; } | SUBPROG { $$ = $1; } ; ident: IDENT { $$ = $1; } | BUILTIN { $$ = $1; } | BUILTIN_TYPE { $$ = $1; } | CALL { $$ = $1; } | CALL_BUILTIN { $$ = $1; } | STACK_MODE { $$ = $1; } ; struct_field: external_name { $$.push_back($1); } | struct_field DOT external_name { $$ = std::move($1); $$.push_back($3); } ; external_name: keyword { $$ = $1; } | ident { $$ = $1; } ; call: CALL "(" ")" { $$ = driver.ctx.make_node($1, @$); } | CALL "(" vargs ")" { $$ = driver.ctx.make_node($1, std::move($3), @$); } | CALL_BUILTIN "(" ")" { $$ = driver.ctx.make_node($1, @$); } | CALL_BUILTIN "(" vargs ")" { $$ = driver.ctx.make_node($1, std::move($3), @$); } | IDENT "(" ")" { error(@1, "Unknown function: " + $1); YYERROR; } | IDENT "(" vargs ")" { error(@1, "Unknown function: " + $1); YYERROR; } | BUILTIN "(" ")" { error(@1, "Unknown function: " + $1); YYERROR; } | BUILTIN "(" vargs ")" { error(@1, "Unknown function: " + $1); YYERROR; } | STACK_MODE "(" ")" { error(@1, "Unknown function: " + $1); YYERROR; } | STACK_MODE "(" vargs ")" { error(@1, "Unknown function: " + $1); YYERROR; } ; map: MAP { $$ = driver.ctx.make_node($1, @$); } | MAP "[" vargs "]" { if ($3.size() > 1) { auto t = driver.ctx.make_node(std::move($3), @$); $$ = driver.ctx.make_node($1, *t, @$); } else { $$ = driver.ctx.make_node($1, *$3.back(), @$); } } ; var: VAR { $$ = driver.ctx.make_node($1, @$); } ; map_or_var: var { $$ = $1; } | map { $$ = $1; } ; vargs: vargs "," expr { $$ = std::move($1); $$.push_back($3); } | expr { $$ = ast::ExpressionList{$1}; } ; compound_op: LEFTASSIGN { $$ = ast::Operator::LEFT; } | BANDASSIGN { $$ = ast::Operator::BAND; } | BORASSIGN { $$ = ast::Operator::BOR; } | BXORASSIGN { $$ = ast::Operator::BXOR; } | DIVASSIGN { $$ = ast::Operator::DIV; } | MINUSASSIGN { $$ = ast::Operator::MINUS; } | MODASSIGN { $$ = ast::Operator::MOD; } | MULASSIGN { $$ = ast::Operator::MUL; } | PLUSASSIGN { $$ = ast::Operator::PLUS; } | RIGHTASSIGN { $$ = ast::Operator::RIGHT; } ; %% void bpftrace::Parser::error(const location &l, const std::string &m) { driver.error(l, m); } bpftrace-0.23.2/src/pcap_writer.cpp000066400000000000000000000024651477746507000172260ustar00rootroot00000000000000#include "pcap_writer.h" #ifdef HAVE_LIBPCAP #include namespace bpftrace { bool PCAPwriter::open(std::string file) { pd_ = pcap_open_dead(DLT_RAW, 65535); if (!pd_) return false; pdumper_ = pcap_dump_open(pd_, file.c_str()); return true; } void PCAPwriter::close() { pcap_close(pd_); pcap_dump_close(pdumper_); } #define NSEC_PER_SEC 1000000000L #define NSEC_PER_USEC 1000L bool PCAPwriter::write(uint64_t ns, void *pkt, unsigned int size) { time_t secs, usecs, nsecs = ns; secs = nsecs / NSEC_PER_SEC; nsecs -= secs * NSEC_PER_SEC; usecs = nsecs / NSEC_PER_USEC; struct pcap_pkthdr hdr = { .ts = { .tv_sec = secs, .tv_usec = usecs, }, .caplen = size, .len = size, }; pcap_dump(reinterpret_cast(pdumper_), &hdr, static_cast(pkt)); return true; } }; // namespace bpftrace #else namespace bpftrace { bool PCAPwriter::open(std::string file __attribute__((__unused__))) { return false; } void PCAPwriter::close(void) { } bool PCAPwriter::write(uint64_t ns __attribute__((__unused__)), void *pkt __attribute__((__unused__)), unsigned int size __attribute__((__unused__))) { return false; } }; // namespace bpftrace #endif // HAVE_LIBPCAP bpftrace-0.23.2/src/pcap_writer.h000066400000000000000000000006741477746507000166730ustar00rootroot00000000000000#pragma once #include #include struct pcap; typedef struct pcap pcap_t; struct pcap_dumper; typedef struct pcap_dumper pcap_dumper_t; namespace bpftrace { class PCAPwriter { public: PCAPwriter() { } ~PCAPwriter() { } bool open(std::string file); void close(void); bool write(uint64_t ns, void *pkt, unsigned int size); private: pcap_t *pd_; pcap_dumper_t *pdumper_; }; }; // namespace bpftrace bpftrace-0.23.2/src/printf.cpp000066400000000000000000000121201477746507000161760ustar00rootroot00000000000000#include "printf.h" #include "log.h" #include "printf_format_types.h" #include "struct.h" #include namespace bpftrace { PrintableString::PrintableString(std::string value, std::optional buffer_size, const char *trunc_trailer) : value_(std::move(value)) { // Add a trailer if string is truncated // // The heuristic we use is to check if the string exactly fits inside // the buffer (NUL included). If it does, we assume it was truncated. // This is obviously not a perfect heuristic, but it solves the majority // case well enough and is simple to implement. if (buffer_size && (value_.size() + 1 == *buffer_size)) value_ += trunc_trailer; } int PrintableString::print(char *buf, size_t size, const char *fmt, Type, ArgumentType) { return snprintf(buf, size, fmt, value_.c_str()); } int PrintableBuffer::print(char *buf, size_t size, const char *fmt, Type, ArgumentType) { return snprintf( buf, size, fmt, hex_format_buffer(value_.data(), value_.size(), keep_ascii_, escape_hex_) .c_str()); } void PrintableBuffer::keep_ascii(bool value) { keep_ascii_ = value; } void PrintableBuffer::escape_hex(bool value) { escape_hex_ = value; } int PrintableCString::print(char *buf, size_t size, const char *fmt, Type, ArgumentType) { return snprintf(buf, size, fmt, value_); } int PrintableInt::print(char *buf, size_t size, const char *fmt, Type, ArgumentType expected_type) { // Since the value is internally always stored as a 64-bit integer, a cast is // needed to ensure that the type of the argument passed to snprintf matches // the format specifier. // For example, an int64_t argument may be pushed onto the stack while an int // is stored in a register, in which case "%d" would print the wrong value if // we used value_ without an explicit cast. switch (expected_type) { case ArgumentType::CHAR: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::SHORT: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::INT: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::LONG: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::LONG_LONG: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::INTMAX_T: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::SIZE_T: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::PTRDIFF_T: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::POINTER: return snprintf(buf, size, fmt, reinterpret_cast(value_)); case ArgumentType::UNKNOWN: return snprintf(buf, size, fmt, value_); } __builtin_unreachable(); } int PrintableSInt::print(char *buf, size_t size, const char *fmt, Type, ArgumentType expected_type) { switch (expected_type) { case ArgumentType::CHAR: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::SHORT: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::INT: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::LONG: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::LONG_LONG: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::INTMAX_T: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::SIZE_T: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::PTRDIFF_T: return snprintf(buf, size, fmt, static_cast(value_)); case ArgumentType::POINTER: return snprintf(buf, size, fmt, reinterpret_cast(value_)); case ArgumentType::UNKNOWN: return snprintf(buf, size, fmt, value_); } __builtin_unreachable(); } int PrintableEnum::print(char *buf, size_t size, const char *fmt, Type token, ArgumentType expected_type) { switch (token) { case Type::integer: return PrintableInt(value_).print(buf, size, fmt, token, expected_type); case Type::string: return snprintf(buf, size, fmt, name_.c_str()); default: LOG(BUG) << "Invalid token type for enum"; __builtin_unreachable(); } } } // namespace bpftrace bpftrace-0.23.2/src/printf.h000066400000000000000000000057001477746507000156510ustar00rootroot00000000000000#pragma once #include #include #include #include "ast/ast.h" #include "printf_format_types.h" #include "types.h" namespace bpftrace { static const std::string generate_pattern_string() { std::string pattern = "%-?[0-9]*(\\.[0-9]+)?("; for (const auto& e : printf_format_types) { pattern += e.first + "|"; } pattern.pop_back(); // for the last "|" pattern += ")"; return pattern; } const std::regex format_specifier_re(generate_pattern_string()); struct Field; enum class ArgumentType { UNKNOWN = 0, CHAR, SHORT, INT, LONG, LONG_LONG, INTMAX_T, SIZE_T, PTRDIFF_T, POINTER, }; class IPrintable { public: virtual ~IPrintable() {}; virtual int print(char* buf, size_t size, const char* fmt, Type token, ArgumentType expected_type = ArgumentType::UNKNOWN) = 0; }; class PrintableString : public virtual IPrintable { public: PrintableString(std::string value, std::optional buffer_size = std::nullopt, const char* trunc_trailer = nullptr); int print(char* buf, size_t size, const char* fmt, Type, ArgumentType) override; private: 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, Type, ArgumentType) override; void keep_ascii(bool value); void escape_hex(bool value); private: std::vector value_; bool keep_ascii_ = true; bool escape_hex_ = true; }; class PrintableCString : public virtual IPrintable { public: PrintableCString(char* value) : value_(value) { } int print(char* buf, size_t size, const char* fmt, Type, ArgumentType) override; private: char* value_; }; class PrintableInt : public virtual IPrintable { public: PrintableInt(uint64_t value) : value_(value) { } int print(char* buf, size_t size, const char* fmt, Type token, ArgumentType expected_type) override; private: uint64_t value_; }; class PrintableSInt : public virtual IPrintable { public: PrintableSInt(int64_t value) : value_(value) { } int print(char* buf, size_t size, const char* fmt, Type token, ArgumentType expected_type) override; private: int64_t value_; }; class PrintableEnum : public virtual IPrintable { public: PrintableEnum(uint64_t value, std::string name) : name_(name), value_(value) { } int print(char* buf, size_t size, const char* fmt, Type token, ArgumentType expected_type) override; private: std::string name_; uint64_t value_; }; } // namespace bpftrace bpftrace-0.23.2/src/printf_format_types.h000066400000000000000000000051171477746507000204470ustar00rootroot00000000000000#pragma once #include #include "types.h" namespace bpftrace { // clang-format off // valid printf length + specifier combinations // it's done like this because as of this writing, C++ doesn't have a builtin way to do // 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", "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}, {"rh", 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} }; // bpf_trace_printk_format_types is a subset of printf_format_types that contains valid types for bpf_trace_printk() // see iovisor/bcc BTypeVisitor::checkFormatSpecifiers const std::unordered_map bpf_trace_printk_format_types = { {"d", Type::integer}, {"u", Type::integer}, {"x", Type::integer}, {"ld", Type::integer}, {"lu", Type::integer}, {"lx", Type::integer}, {"lld", Type::integer}, {"llu", Type::integer}, {"llx", Type::integer}, {"p", Type::integer}, {"s", Type::string} }; // clang-format on } // namespace bpftrace bpftrace-0.23.2/src/probe_matcher.cpp000066400000000000000000000443001477746507000175130ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "bpftrace.h" #include "cxxdemangler/cxxdemangler.h" #include "dwarf_parser.h" #include "log.h" #include "probe_matcher.h" #include "scopeguard.h" #include "tracefs.h" #include "utils.h" #include #include #include namespace bpftrace { static int add_symbol(const char* symname, uint64_t /*start*/, uint64_t /*size*/, void* payload) { auto syms = static_cast*>(payload); syms->insert(std::string(symname)); return 0; } // Finds all matches of search_input in the provided input stream. std::set ProbeMatcher::get_matches_in_stream( const std::string& search_input, std::istream& symbol_stream, bool demangle_symbols, const char delim) { bool start_wildcard, end_wildcard; auto tokens = get_wildcard_tokens(search_input, start_wildcard, end_wildcard); // Since demangled_name contains function parameters, we need to remove // them unless the user specified '(' in the search input (i.e. wants // to match against the parameters explicitly). // Only used for C++ when demangling is enabled. auto has_parameter = [](const std::string& token) { return token.find('(') != std::string::npos; }; const bool truncate_parameters = std::none_of(tokens.begin(), tokens.end(), has_parameter); std::string line; std::set matches; while (std::getline(symbol_stream, line, delim)) { if (!wildcard_match(line, tokens, start_wildcard, end_wildcard)) { if (demangle_symbols) { auto fun_line = line; auto prefix = fun_line.find(':') != std::string::npos ? erase_prefix(fun_line) + ":" : ""; if (symbol_has_cpp_mangled_signature(fun_line)) { char* demangled_name = cxxdemangle(fun_line.c_str()); if (!demangled_name) continue; SCOPE_EXIT { ::free(demangled_name); }; // Match against the demanled name. std::string match_line = prefix + demangled_name; if (truncate_parameters) { erase_parameter_list(match_line); } if (wildcard_match( match_line, tokens, start_wildcard, end_wildcard)) { goto out; } } } continue; } out: // skip the ".part.N" kprobe variants, as they can't be traced: if (line.find(".part.") != std::string::npos) continue; matches.insert(line); } return matches; } // Get matches of search_input (containing a wildcard) for a given probe_type. // probe_type determines where to take the candidate matches from. // Some probe types (e.g. uprobe) require target to be specified. std::set ProbeMatcher::get_matches_for_probetype( const ProbeType& probe_type, const std::string& target, const std::string& search_input, bool demangle_symbols) { std::unique_ptr symbol_stream; switch (probe_type) { case ProbeType::kprobe: case ProbeType::kretprobe: { if (!target.empty()) symbol_stream = get_symbols_from_traceable_funcs(true); else symbol_stream = get_symbols_from_traceable_funcs(false); break; } case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: { symbol_stream = get_func_symbols_from_file(bpftrace_->pid(), target); break; } case ProbeType::tracepoint: { symbol_stream = get_symbols_from_file(tracefs::available_events()); break; } case ProbeType::rawtracepoint: { symbol_stream = get_symbols_from_file(tracefs::available_events()); symbol_stream = adjust_rawtracepoint(*symbol_stream); break; } case ProbeType::usdt: { symbol_stream = get_symbols_from_usdt(bpftrace_->pid(), target); break; } case ProbeType::software: { symbol_stream = get_symbols_from_list(SW_PROBE_LIST); break; } case ProbeType::hardware: { symbol_stream = get_symbols_from_list(HW_PROBE_LIST); break; } case ProbeType::fentry: case ProbeType::fexit: { // If BTF is not parsed, yet, read available_filter_functions instead. // This is useful as we will use the result to extract the list of // potentially used kernel modules and then only parse BTF for them. if (bpftrace_->has_btf_data()) symbol_stream = bpftrace_->btf_->get_all_funcs(); else { symbol_stream = get_symbols_from_traceable_funcs(true); } break; } case ProbeType::iter: { if (!bpftrace_->has_btf_data()) break; std::string ret; auto iters = bpftrace_->btf_->get_all_iters(); for (auto& iter : iters) { // second check if (bpftrace_->feature_->has_iter(iter)) ret += iter + "\n"; else LOG(WARNING) << "The kernel contains bpf_iter__" << iter << " struct but does not support loading an iterator" " program against it. Please report this bug."; } symbol_stream = std::make_unique(ret); break; } case ProbeType::interval: case ProbeType::profile: { std::string ret; for (auto& unit : TIME_UNITS) ret += unit + ":\n"; symbol_stream = std::make_unique(ret); break; } case ProbeType::special: return { target + ":" }; default: return {}; } if (symbol_stream) return get_matches_in_stream(search_input, *symbol_stream, demangle_symbols); else return {}; } // Find all matches of search_input in set std::set ProbeMatcher::get_matches_in_set( const std::string& search_input, const std::set& set) { std::string stream_in; // Strings in the set may contain a newline character, so we use '$' // as a delimiter. for (auto& str : set) stream_in.append(str + "$"); std::istringstream stream(stream_in); return get_matches_in_stream(search_input, stream, false, '$'); } std::unique_ptr ProbeMatcher::get_symbols_from_file( const std::string& path) const { auto file = std::make_unique(path); if (file->fail()) { LOG(WARNING) << "Could not read symbols from " << path << ": " << strerror(errno); return nullptr; } return file; } std::unique_ptr ProbeMatcher::get_symbols_from_traceable_funcs( bool with_modules) const { std::string funcs; for (auto& func_mod : bpftrace_->get_traceable_funcs()) { if (with_modules) { for (auto& mod : func_mod.second) funcs += mod + ":" + func_mod.first + "\n"; } else { funcs += func_mod.first + "\n"; } } return std::make_unique(funcs); } std::unique_ptr ProbeMatcher::get_func_symbols_from_file( int pid, const std::string& path) const { if (path.empty()) return std::make_unique(""); std::vector real_paths; if (path == "*") { if (pid > 0) real_paths = get_mapped_paths_for_pid(pid); else real_paths = get_mapped_paths_for_running_pids(); } else if (path.find('*') != std::string::npos) real_paths = resolve_binary_path(path, pid); else real_paths.push_back(path); struct bcc_symbol_option symbol_option; memset(&symbol_option, 0, sizeof(symbol_option)); symbol_option.use_debug_file = 1; symbol_option.check_debug_file_crc = 1; symbol_option.use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC); std::string result; for (auto& real_path : real_paths) { std::set syms; // Workaround: bcc_elf_foreach_sym() can return the same symbol twice if // it's also found in debug info (#1138), so a std::set is used here (and in // the add_symbol callback) to ensure that each symbol will be unique in the // returned string. int err = bcc_elf_foreach_sym( real_path.c_str(), add_symbol, &symbol_option, &syms); if (err) { LOG(WARNING) << "Could not list function symbols: " + real_path; } for (auto& sym : syms) result += real_path + ":" + sym + "\n"; } return std::make_unique(result); } std::unique_ptr ProbeMatcher::get_symbols_from_usdt( int pid, const std::string& target) const { std::string probes; usdt_probe_list usdt_probes; if (pid > 0) usdt_probes = USDTHelper::probes_for_pid(pid); else if (target == "*") usdt_probes = USDTHelper::probes_for_all_pids(); else if (!target.empty()) { std::vector real_paths; if (target.find('*') != std::string::npos) real_paths = resolve_binary_path(target); else real_paths.push_back(target); for (auto& real_path : real_paths) { auto target_usdt_probes = USDTHelper::probes_for_path(real_path); usdt_probes.insert(usdt_probes.end(), target_usdt_probes.begin(), target_usdt_probes.end()); } } for (auto const& usdt_probe : usdt_probes) { std::string path = usdt_probe.path; std::string provider = usdt_probe.provider; std::string fname = usdt_probe.name; probes += path + ":" + provider + ":" + fname + "\n"; } return std::make_unique(probes); } std::unique_ptr ProbeMatcher::get_symbols_from_list( const std::vector& probes_list) const { std::string symbols; for (auto& probe : probes_list) { symbols += probe.path + ":\n"; if (!probe.alias.empty()) symbols += probe.alias + ":\n"; } return std::make_unique(symbols); } // Get list of kernel probe types for the purpose of listing. // Ignore return probes and aliases. std::unique_ptr ProbeMatcher::kernel_probe_list() { std::string probes; for (auto& p : PROBE_LIST) { if (!p.show_in_kernel_list) { continue; } if (p.type == ProbeType::fentry) { // fentry must be available if (bpftrace_->feature_->has_fentry()) probes += p.name + "\n"; } else { probes += p.name + "\n"; } } return std::make_unique(probes); } // Get list of userspace probe types for the purpose of listing. // Ignore return probes. std::unique_ptr ProbeMatcher::userspace_probe_list() { std::string probes; for (auto& p : PROBE_LIST) { if (p.show_in_userspace_list) { probes += p.name + "\n"; } } return std::make_unique(probes); } FuncParamLists ProbeMatcher::get_tracepoints_params( const std::set& tracepoints) { FuncParamLists params; for (auto& tracepoint : tracepoints) { auto event = tracepoint; auto category = erase_prefix(event); std::string format_file_path = tracefs::event_format_file(category, event); std::ifstream format_file(format_file_path.c_str()); std::string line; if (format_file.fail()) { LOG(ERROR) << "tracepoint format file not found: " << format_file_path; return {}; } // Skip lines until the first empty line do { getline(format_file, line); } while (line.length() > 0); while (getline(format_file, line)) { if (line.find("\tfield:") == 0) { size_t col_pos = line.find(':') + 1; params[tracepoint].push_back( line.substr(col_pos, line.find(';') - col_pos)); } } } return params; } FuncParamLists ProbeMatcher::get_iters_params( const std::set& iters) { const std::string prefix = "vmlinux:bpf_iter_"; FuncParamLists params; std::set funcs; for (auto& iter : iters) funcs.insert(prefix + iter); params = bpftrace_->btf_->get_params(funcs); for (auto func : funcs) { // delete `int retval` params[func].pop_back(); // delete `struct bpf_iter_meta * meta` params[func].erase(params[func].begin()); // restore key value auto param = params.extract(func); param.key() = func.substr(prefix.size()); params.insert(std::move(param)); } return params; } FuncParamLists ProbeMatcher::get_uprobe_params( const std::set& uprobes) { FuncParamLists params; static std::set warned_paths; for (auto& match : uprobes) { std::string fun = match; std::string path = erase_prefix(fun); auto dwarf = Dwarf::GetFromBinary(nullptr, path); if (dwarf) params.emplace(match, dwarf->get_function_params(fun)); else { if (warned_paths.insert(path).second) LOG(WARNING) << "No DWARF found for \"" << path << "\"" << ", cannot show parameter info"; } } return params; } void ProbeMatcher::list_probes(ast::Program* prog) { for (auto* probe : prog->probes) { for (auto* ap : probe->attach_points) { auto matches = get_matches_for_ap(*ap); auto probe_type = probetype(ap->provider); FuncParamLists param_lists; if (bt_verbose) { if (probe_type == ProbeType::tracepoint) param_lists = get_tracepoints_params(matches); else if (probe_type == ProbeType::fentry || probe_type == ProbeType::fexit) param_lists = bpftrace_->btf_->get_params(matches); else if (probe_type == ProbeType::iter) param_lists = get_iters_params(matches); else if (probe_type == ProbeType::uprobe) param_lists = get_uprobe_params(matches); } for (auto& match : matches) { std::string match_print = match; if (ap->lang == "cpp") { std::string target = erase_prefix(match_print); char* demangled_name = cxxdemangle(match_print.c_str()); SCOPE_EXIT { ::free(demangled_name); }; // demangled name may contain symbols not accepted by the attach point // parser, so surround it with quotes to make the entry directly // usable as an attach point auto func = demangled_name ? "\"" + std::string(demangled_name) + "\"" : match_print; match_print = target + ":" + ap->lang + ":" + func; } std::cout << probe_type << ":" << match_print << std::endl; if (bt_verbose) { for (auto& param : param_lists[match]) std::cout << " " << param << std::endl; } } } } } std::set ProbeMatcher::get_matches_for_ap( const ast::AttachPoint& attach_point) { std::string search_input; switch (probetype(attach_point.provider)) { case ProbeType::kprobe: case ProbeType::kretprobe: { if (!attach_point.target.empty()) search_input = attach_point.target + ":" + attach_point.func; else search_input = attach_point.func; break; } case ProbeType::iter: case ProbeType::rawtracepoint: { search_input = attach_point.func; break; } case ProbeType::special: case ProbeType::uprobe: case ProbeType::uretprobe: case ProbeType::watchpoint: case ProbeType::asyncwatchpoint: case ProbeType::tracepoint: case ProbeType::fentry: case ProbeType::fexit: { // 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; } case ProbeType::hardware: case ProbeType::software: case ProbeType::profile: case ProbeType::interval: { search_input = attach_point.target + ":"; break; } case ProbeType::usdt: { auto target = attach_point.target; // If PID is specified, targets in symbol_stream will have the // "/proc//root" prefix followed by an absolute path, so we make the // target absolute and add a leading wildcard. if (bpftrace_->pid() > 0) { if (!target.empty()) { if (auto abs_target = abs_path(target)) target = "*" + abs_target.value(); } else target = "*"; } auto ns = attach_point.ns.empty() ? "*" : attach_point.ns; search_input = target + ":" + ns + ":" + attach_point.func; break; } case ProbeType::invalid: throw WildcardException( "Wildcard matches aren't available on probe type '" + attach_point.provider + "'"); } return get_matches_for_probetype(probetype(attach_point.provider), attach_point.target, search_input, attach_point.lang == "cpp"); } std::set ProbeMatcher::expand_probetype_kernel( const std::string& probe_type) { if (has_wildcard(probe_type)) return get_matches_in_stream(probe_type, *kernel_probe_list()); else return { probe_type }; } std::set ProbeMatcher::expand_probetype_userspace( const std::string& probe_type) { if (has_wildcard(probe_type)) return get_matches_in_stream(probe_type, *userspace_probe_list()); else return { probe_type }; } void ProbeMatcher::list_structs(const std::string& search) { auto structs = bpftrace_->btf_->get_all_structs(); std::string search_input = search; // If verbose is on, structs will contain full definitions if (bt_verbose) search_input += " *{*}*"; for (auto& match : get_matches_in_set(search_input, structs)) std::cout << match << std::endl; } std::unique_ptr ProbeMatcher::adjust_rawtracepoint( std::istream& symbol_list) const { auto new_list = std::make_unique(); std::string line; while (std::getline(symbol_list, line, '\n')) { if ((line.find("syscalls:sys_enter_") != std::string::npos) || (line.find("syscalls:sys_exit_") != std::string::npos)) continue; erase_prefix(line); *new_list << line << "\n"; } return new_list; } } // namespace bpftrace bpftrace-0.23.2/src/probe_matcher.h000066400000000000000000000114231477746507000171600ustar00rootroot00000000000000#pragma once #include "ast/ast.h" #include #include #include namespace bpftrace { struct ProbeListItem { std::string path; std::string alias; uint32_t type; uint64_t defaultp; }; // clang-format off const std::vector SW_PROBE_LIST = { { "alignment-faults", "", PERF_COUNT_SW_ALIGNMENT_FAULTS, 1 }, { "bpf-output", "", PERF_COUNT_SW_BPF_OUTPUT, 1 }, { "context-switches", "cs", PERF_COUNT_SW_CONTEXT_SWITCHES, 1000 }, { "cpu-clock", "cpu", PERF_COUNT_SW_CPU_CLOCK, 1000000 }, { "cpu-migrations", "", PERF_COUNT_SW_CPU_MIGRATIONS, 1 }, { "dummy", "", PERF_COUNT_SW_DUMMY, 1 }, { "emulation-faults", "", PERF_COUNT_SW_EMULATION_FAULTS, 1 }, { "major-faults", "", PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1 }, { "minor-faults", "", PERF_COUNT_SW_PAGE_FAULTS_MIN, 100 }, { "page-faults", "faults", PERF_COUNT_SW_PAGE_FAULTS, 100 }, { "task-clock", "", PERF_COUNT_SW_TASK_CLOCK, 1 }, }; const std::vector HW_PROBE_LIST = { { "backend-stalls", "", PERF_COUNT_HW_STALLED_CYCLES_BACKEND, 1000000 }, { "branch-instructions", "branches", PERF_COUNT_HW_BRANCH_INSTRUCTIONS, 100000 }, { "branch-misses", "", PERF_COUNT_HW_BRANCH_MISSES, 100000 }, { "bus-cycles", "", PERF_COUNT_HW_BUS_CYCLES, 100000 }, { "cache-misses", "", PERF_COUNT_HW_CACHE_MISSES, 1000000 }, { "cache-references", "", PERF_COUNT_HW_CACHE_REFERENCES, 1000000 }, { "cpu-cycles", "cycles", PERF_COUNT_HW_CPU_CYCLES, 1000000 }, { "frontend-stalls", "", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, 1000000 }, { "instructions", "", PERF_COUNT_HW_INSTRUCTIONS, 1000000 }, { "ref-cycles", "", PERF_COUNT_HW_REF_CPU_CYCLES, 1000000 } }; // clang-format on const std::unordered_set TIME_UNITS = { "s", "ms", "us", "hz" }; const std::unordered_set SIGNALS = { "SIGUSR1" }; class BPFtrace; typedef std::map> FuncParamLists; class ProbeMatcher { public: explicit ProbeMatcher(BPFtrace *bpftrace) : bpftrace_(bpftrace) { } virtual ~ProbeMatcher() = default; // Get all matches for attach point containing a wildcard. // The output strings format depends on the probe type. std::set get_matches_for_ap( const ast::AttachPoint &attach_point); // Expanding probe type containing a wildcard. std::set expand_probetype_kernel(const std::string &probe_type); std::set expand_probetype_userspace( const std::string &probe_type); // Match all probes in prog and print them to stdout. void list_probes(ast::Program *prog); // Print definitions of structures matching search. void list_structs(const std::string &search); const BPFtrace *bpftrace_; private: std::set get_matches_in_stream(const std::string &search_input, std::istream &symbol_stream, bool demangle_symbols = true, const char delim = '\n'); std::set get_matches_for_probetype( const ProbeType &probe_type, const std::string &target, const std::string &search_input, bool demangle_symbols); std::set get_matches_in_set(const std::string &search_input, const std::set &set); virtual std::unique_ptr get_symbols_from_traceable_funcs( bool with_modules = false) const; virtual std::unique_ptr get_symbols_from_file( const std::string &path) const; virtual std::unique_ptr get_func_symbols_from_file( int pid, const std::string &path) const; virtual std::unique_ptr get_symbols_from_usdt( int pid, const std::string &target) const; virtual std::unique_ptr get_symbols_from_list( const std::vector &probes_list) const; virtual std::unique_ptr adjust_rawtracepoint( std::istream &symbol_list) const; std::unique_ptr get_iter_symbols(void) const; std::unique_ptr kernel_probe_list(); std::unique_ptr userspace_probe_list(); FuncParamLists get_tracepoints_params( const std::set &tracepoints); FuncParamLists get_iters_params(const std::set &iters); FuncParamLists get_uprobe_params(const std::set &uprobes); }; } // namespace bpftrace bpftrace-0.23.2/src/procmon.cpp000066400000000000000000000040431477746507000163560ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "procmon.h" #include "utils.h" namespace bpftrace { #ifndef __NR_pidfd_open #define __NR_pidfd_open 434 #endif static std::system_error SYS_ERROR(std::string msg) { return std::system_error(errno, std::generic_category(), msg); } static inline int pidfd_open(int pid, unsigned int flags) { return syscall(__NR_pidfd_open, pid, flags); } ProcMon::ProcMon(pid_t pid) { setup(pid); } void ProcMon::setup(pid_t pid) { pid_ = pid; int pidfd = pidfd_open(pid, 0); // Fall back to polling if pidfds or anon inodes are not supported if (pidfd >= 0) { pidfd_ = pidfd; return; } else if (errno != ENOSYS) { if (errno == ESRCH) throw SYS_ERROR(""); /* use default error message for ESRCH */ throw SYS_ERROR("Failed to pidfd_open pid"); } int ret = snprintf(proc_path_, sizeof(proc_path_) / sizeof(proc_path_[0]), "/proc/%d/status", pid); if (ret < 0) { throw std::runtime_error("failed to snprintf"); } if (!is_alive()) throw std::runtime_error("No such process: " + std::to_string(pid)); } ProcMon::~ProcMon() { if (pidfd_ >= 0) close(pidfd_); } bool ProcMon::is_alive() { // store death to avoid pid reuse issues on polling /proc if (died_) return false; if (pidfd_ > -1) { struct pollfd pollfd; pollfd.fd = pidfd_; pollfd.events = POLLIN; int ret; while ((ret = poll(&pollfd, 1, 0)) < 0 && errno == EINTR) ; if (ret < 0) throw SYS_ERROR("poll pidfd"); else if (ret == 0) // no change, so must be alive return true; died_ = true; return false; } int fd = open(proc_path_, 0, O_RDONLY); if (fd < 0) { if (errno == ENOENT) { died_ = true; return false; } std::string msg = "Failed to open " + std::string(proc_path_); throw SYS_ERROR(msg); } close(fd); return true; } } // namespace bpftrace bpftrace-0.23.2/src/procmon.h000066400000000000000000000015351477746507000160260ustar00rootroot00000000000000#pragma once #include #include namespace bpftrace { class ProcMonBase { public: ProcMonBase() = default; virtual ~ProcMonBase() = default; // Whether the process is still alive virtual bool is_alive(void) = 0; // pid of the process being monitored pid_t pid(void) { return pid_; }; protected: int pid_ = -1; }; class ProcMon : public ProcMonBase { public: ProcMon(pid_t pid); ~ProcMon() override; // Disallow copying as the internal state will get out of sync which will // cause issues. ProcMon(const ProcMon&) = delete; ProcMon& operator=(const ProcMon&) = delete; ProcMon(ProcMon&&) = delete; ProcMon& operator=(ProcMon&&) = delete; bool is_alive(void) override; private: int pidfd_ = -1; char proc_path_[32]; bool died_ = false; void setup(pid_t pid); }; } // namespace bpftrace bpftrace-0.23.2/src/required_resources.cpp000066400000000000000000000016231477746507000206140ustar00rootroot00000000000000#include "required_resources.h" #include #include #include #include #include #include #include #include #include "bpftrace.h" #include "log.h" #include "utils.h" namespace bpftrace { void RequiredResources::save_state(std::ostream &out) const { cereal::BinaryOutputArchive archive(out); archive(*this); } void RequiredResources::load_state(std::istream &in) { cereal::BinaryInputArchive archive(in); archive(*this); } void RequiredResources::load_state(const uint8_t *ptr, size_t len) { auto addr = const_cast(ptr); Membuf mbuf(addr, addr + len); std::istream istream(&mbuf); cereal::BinaryInputArchive archive(istream); archive(*this); } } // namespace bpftrace bpftrace-0.23.2/src/required_resources.h000066400000000000000000000120771477746507000202660ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include "format_string.h" #include "location.hh" #include "struct.h" #include "types.h" namespace bpftrace { class BPFtrace; struct HelperErrorInfo { int func_id = -1; location loc; }; struct LinearHistogramArgs { long min = -1; long max = -1; long step = -1; bool operator==(const LinearHistogramArgs &other) { return min == other.min && max == other.max && step == other.step; } bool operator!=(const LinearHistogramArgs &other) { return !(*this == other); } private: friend class cereal::access; template void serialize(Archive &archive) { archive(min, max, step); } }; struct MapInfo { SizedType key_type; SizedType value_type; std::optional lhist_args; std::optional hist_bits_arg; int id = -1; private: friend class cereal::access; template void serialize(Archive &archive) { archive(key_type, value_type, lhist_args, hist_bits_arg, id); } }; // This class contains script-specific metadata that bpftrace's runtime needs. // // This class is intended to completely encapsulate all of a script's runtime // needs such as maps, async printf argument metadata, etc. An instance of this // class plus the actual bpf bytecode should be all that's necessary to run a // script on another host. class RequiredResources { public: // `save_state()` serializes `RequiredResources` and writes results into // `out`. `load_state()` does the reverse: takes serialized data and loads it // into the current instance. // // NB: The serialized data is not versioned and is not forward/backwards // compatible. // // NB: both the output and input stream must be opened in binary // (std::ios::binary) mode to avoid binary data from being interpreted wrong void save_state(std::ostream &out) const; void load_state(std::istream &in); void load_state(const uint8_t *ptr, size_t len); // Async argument metadata std::vector>> printf_args; std::vector>> system_args; // fmt strings for BPF helpers (bpf_seq_printf, bpf_trace_printk) std::vector bpf_print_fmts; std::vector>> cat_args; std::vector join_args; std::vector time_args; std::vector strftime_args; std::vector cgroup_path_args; std::vector non_map_print_args; std::vector> skboutput_args_; // While max fmtstring args size is not used at runtime, the size // calculation requires taking into account struct alignment semantics, // and that is tricky enough that we want to minimize repetition of // such logic in the codebase. So keep it in resource analysis // rather than duplicating it in CodegenResources. uint64_t max_fmtstring_args_size = 0; // Required for sizing of tuple scratch buffer size_t tuple_buffers = 0; size_t max_tuple_size = 0; // Required for sizing of string scratch buffer size_t str_buffers = 0; // Required for sizing of map value scratch buffers size_t read_map_value_buffers = 0; size_t max_read_map_value_size = 0; size_t max_write_map_value_size = 0; // Required for sizing of variable scratch buffers size_t variable_buffers = 0; size_t max_variable_size = 0; // Required for sizing of map key scratch buffers size_t map_key_buffers = 0; size_t max_map_key_size = 0; // Async argument metadata that codegen creates. Ideally ResourceAnalyser // pass should be collecting this, but it's complex to move the logic. // // Don't add more async arguments here!. std::unordered_map helper_error_info; std::vector probe_ids; // Map metadata std::map maps_info; std::unordered_set needed_global_vars; bool needs_perf_event_map = false; // Probe metadata // // Probe metadata that codegen creates. Ideally ResourceAnalyser pass should // be collecting this, but it's complex to move the logic. std::vector probes; std::unordered_map special_probes; std::vector watchpoint_probes; // List of probes using userspace symbol resolution std::unordered_set probes_using_usym; private: friend class cereal::access; template void serialize(Archive &archive) { archive(system_args, bpf_print_fmts, join_args, time_args, strftime_args, cat_args, non_map_print_args, // Hard to annotate flex types, so skip // helper_error_info, printf_args, probe_ids, maps_info, needed_global_vars, needs_perf_event_map, probes, special_probes); } }; } // namespace bpftrace bpftrace-0.23.2/src/resolve_cgroupid.cpp000066400000000000000000000050261477746507000202560ustar00rootroot00000000000000#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #ifdef HAVE_NAME_TO_HANDLE_AT #include #include #else #include #include #endif #include #include #include #include #include "act_helpers.h" #include "log.h" #include "resolve_cgroupid.h" #include "utils.h" namespace { #ifndef HAVE_NAME_TO_HANDLE_AT struct file_handle { unsigned int handle_bytes; int handle_type; char f_handle[0]; }; int name_to_handle_at(int dirfd, const char *pathname, struct file_handle *handle, int *mount_id, int flags) { return (int)syscall( SYS_name_to_handle_at, dirfd, pathname, handle, mount_id, flags); } #endif // Not embedding file_handle directly in cgid_file_handle, because C++ // has problems with zero-sized array as struct members and // file_handle's f_handle is a zero-sized array. // // Also, not embedding file_handle through the public inheritance, // since checking for member offsets with offsetof within a // non-standard-layout type is conditionally-supported (so compilers // are not required to support it). And we want to do it to make sure // we got the wrapper right. // // Hence open coding the file_handle members directly in // cgid_file_handle and the static asserts following it. struct cgid_file_handle { file_handle *as_file_handle_ptr() { return reinterpret_cast(this); } unsigned int handle_bytes = sizeof(std::uint64_t); int handle_type; std::uint64_t cgid; }; ACTH_ASSERT_SAME_SIZE(cgid_file_handle, file_handle, std::uint64_t); ACTH_ASSERT_SAME_MEMBER(cgid_file_handle, handle_bytes, file_handle, handle_bytes); ACTH_ASSERT_SAME_MEMBER(cgid_file_handle, handle_type, file_handle, handle_type); ACTH_ASSERT_SAME_OFFSET(cgid_file_handle, cgid, file_handle, f_handle); } // namespace namespace bpftrace_linux { std::uint64_t resolve_cgroupid(const std::string &path) { cgid_file_handle cfh; int mount_id; int err = name_to_handle_at( AT_FDCWD, path.c_str(), cfh.as_file_handle_ptr(), &mount_id, 0); if (err < 0) { char *e = std::strerror(errno); throw bpftrace::FatalUserException("Failed to get `cgroupid` for path: \"" + path + "\": " + std::string(e ? e : "")); } return cfh.cgid; } } // namespace bpftrace_linux bpftrace-0.23.2/src/resolve_cgroupid.h000066400000000000000000000002141477746507000177150ustar00rootroot00000000000000#pragma once #include #include namespace bpftrace_linux { std::uint64_t resolve_cgroupid(const std::string &path); } bpftrace-0.23.2/src/resources/000077500000000000000000000000001477746507000162065ustar00rootroot00000000000000bpftrace-0.23.2/src/resources/.clang-format000066400000000000000000000000501477746507000205540ustar00rootroot00000000000000DisableFormat: true SortIncludes: Never bpftrace-0.23.2/src/resources/CMakeLists.txt000066400000000000000000000020701477746507000207450ustar00rootroot00000000000000function(embed_headers output_h output_cpp) set(prologue "#include \n\nnamespace bpftrace {\n") get_filename_component(output_h_name ${output_h} NAME) file(WRITE ${output_h} ${prologue}) file(WRITE ${output_cpp} "#include \"${output_h_name}\"\n\n") file(APPEND ${output_cpp} ${prologue}) file(GLOB headers *.h) foreach(header ${headers}) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${header}) get_filename_component(filename ${header} NAME) string(MAKE_C_IDENTIFIER ${filename} varname) file(READ ${header} contents) file(APPEND ${output_h} "extern const std::string_view ${varname};\n") file(APPEND ${output_cpp} "const std::string_view ${varname} = R\"CONTENTS(${contents})CONTENTS\";\n\n") endforeach() set(epilogue "} // namespace bpftrace") file(APPEND ${output_h} ${epilogue}) file(APPEND ${output_cpp} ${epilogue}) endfunction() embed_headers(${CMAKE_CURRENT_BINARY_DIR}/headers.h ${CMAKE_CURRENT_BINARY_DIR}/headers.cpp) add_library(resources STATIC ${CMAKE_CURRENT_BINARY_DIR}/headers.cpp) bpftrace-0.23.2/src/resources/__stddef_max_align_t.h000066400000000000000000000033521477746507000224730ustar00rootroot00000000000000/*===---- __stddef_max_align_t.h - Definition of max_align_t for modules ---=== * * Copyright (c) 2014 Chandler Carruth * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * *===-----------------------------------------------------------------------=== */ #ifndef __CLANG_MAX_ALIGN_T_DEFINED #define __CLANG_MAX_ALIGN_T_DEFINED #if defined(_MSC_VER) typedef double max_align_t; #elif defined(__APPLE__) typedef long double max_align_t; #else // Define 'max_align_t' to match the GCC definition. typedef struct { long long __clang_max_align_nonce1 __attribute__((__aligned__(__alignof__(long long)))); long double __clang_max_align_nonce2 __attribute__((__aligned__(__alignof__(long double)))); } max_align_t; #endif #endif bpftrace-0.23.2/src/resources/float.h000066400000000000000000000121071477746507000174650ustar00rootroot00000000000000/*===---- float.h - Characteristics of floating point types ----------------=== * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * *===-----------------------------------------------------------------------=== */ #ifndef __FLOAT_H #define __FLOAT_H /* If we're on MinGW, fall back to the system's float.h, which might have * additional definitions provided for Windows. * For more details see http://msdn.microsoft.com/en-us/library/y0ybw9fy.aspx * * Also fall back on Darwin to allow additional definitions and * implementation-defined values. */ #if (defined(__APPLE__) || (defined(__MINGW32__) || defined(_MSC_VER))) && \ __STDC_HOSTED__ && __has_include_next() /* Prior to Apple's 10.7 SDK, float.h SDK header used to apply an extra level * of #include_next to keep Metrowerks compilers happy. Avoid this * extra indirection. */ #ifdef __APPLE__ #define _FLOAT_H_ #endif # include_next /* Undefine anything that we'll be redefining below. */ # undef FLT_EVAL_METHOD # undef FLT_ROUNDS # undef FLT_RADIX # undef FLT_MANT_DIG # undef DBL_MANT_DIG # undef LDBL_MANT_DIG # if __STDC_VERSION__ >= 199901L || !defined(__STRICT_ANSI__) # undef DECIMAL_DIG # endif # undef FLT_DIG # undef DBL_DIG # undef LDBL_DIG # undef FLT_MIN_EXP # undef DBL_MIN_EXP # undef LDBL_MIN_EXP # undef FLT_MIN_10_EXP # undef DBL_MIN_10_EXP # undef LDBL_MIN_10_EXP # undef FLT_MAX_EXP # undef DBL_MAX_EXP # undef LDBL_MAX_EXP # undef FLT_MAX_10_EXP # undef DBL_MAX_10_EXP # undef LDBL_MAX_10_EXP # undef FLT_MAX # undef DBL_MAX # undef LDBL_MAX # undef FLT_EPSILON # undef DBL_EPSILON # undef LDBL_EPSILON # undef FLT_MIN # undef DBL_MIN # undef LDBL_MIN # if __STDC_VERSION__ >= 201112L || !defined(__STRICT_ANSI__) # undef FLT_TRUE_MIN # undef DBL_TRUE_MIN # undef LDBL_TRUE_MIN # undef FLT_DECIMAL_DIG # undef DBL_DECIMAL_DIG # undef LDBL_DECIMAL_DIG # endif #endif /* Characteristics of floating point types, C99 5.2.4.2.2 */ #define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ #define FLT_ROUNDS (__builtin_flt_rounds()) #define FLT_RADIX __FLT_RADIX__ #define FLT_MANT_DIG __FLT_MANT_DIG__ #define DBL_MANT_DIG __DBL_MANT_DIG__ #define LDBL_MANT_DIG __LDBL_MANT_DIG__ #if __STDC_VERSION__ >= 199901L || !defined(__STRICT_ANSI__) # define DECIMAL_DIG __DECIMAL_DIG__ #endif #define FLT_DIG __FLT_DIG__ #define DBL_DIG __DBL_DIG__ #define LDBL_DIG __LDBL_DIG__ #define FLT_MIN_EXP __FLT_MIN_EXP__ #define DBL_MIN_EXP __DBL_MIN_EXP__ #define LDBL_MIN_EXP __LDBL_MIN_EXP__ #define FLT_MIN_10_EXP __FLT_MIN_10_EXP__ #define DBL_MIN_10_EXP __DBL_MIN_10_EXP__ #define LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__ #define FLT_MAX_EXP __FLT_MAX_EXP__ #define DBL_MAX_EXP __DBL_MAX_EXP__ #define LDBL_MAX_EXP __LDBL_MAX_EXP__ #define FLT_MAX_10_EXP __FLT_MAX_10_EXP__ #define DBL_MAX_10_EXP __DBL_MAX_10_EXP__ #define LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__ #define FLT_MAX __FLT_MAX__ #define DBL_MAX __DBL_MAX__ #define LDBL_MAX __LDBL_MAX__ #define FLT_EPSILON __FLT_EPSILON__ #define DBL_EPSILON __DBL_EPSILON__ #define LDBL_EPSILON __LDBL_EPSILON__ #define FLT_MIN __FLT_MIN__ #define DBL_MIN __DBL_MIN__ #define LDBL_MIN __LDBL_MIN__ #if __STDC_VERSION__ >= 201112L || !defined(__STRICT_ANSI__) # define FLT_TRUE_MIN __FLT_DENORM_MIN__ # define DBL_TRUE_MIN __DBL_DENORM_MIN__ # define LDBL_TRUE_MIN __LDBL_DENORM_MIN__ # define FLT_DECIMAL_DIG __FLT_DECIMAL_DIG__ # define DBL_DECIMAL_DIG __DBL_DECIMAL_DIG__ # define LDBL_DECIMAL_DIG __LDBL_DECIMAL_DIG__ #endif #ifdef __STDC_WANT_IEC_60559_TYPES_EXT__ # define FLT16_MANT_DIG __FLT16_MANT_DIG__ # define FLT16_DECIMAL_DIG __FLT16_DECIMAL_DIG__ # define FLT16_DIG __FLT16_DIG__ # define FLT16_MIN_EXP __FLT16_MIN_EXP__ # define FLT16_MIN_10_EXP __FLT16_MIN_10_EXP__ # define FLT16_MAX_EXP __FLT16_MAX_EXP__ # define FLT16_MAX_10_EXP __FLT16_MAX_10_EXP__ # define FLT16_MAX __FLT16_MAX__ # define FLT16_EPSILON __FLT16_EPSILON__ # define FLT16_MIN __FLT16_MIN__ # define FLT16_TRUE_MIN __FLT16_TRUE_MIN__ #endif /* __STDC_WANT_IEC_60559_TYPES_EXT__ */ #endif /* __FLOAT_H */ bpftrace-0.23.2/src/resources/limits.h000066400000000000000000000072261477746507000176670ustar00rootroot00000000000000/*===---- limits.h - Standard header for integer sizes --------------------===*\ * * Copyright (c) 2009 Chris Lattner * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * \*===----------------------------------------------------------------------===*/ #ifndef __CLANG_LIMITS_H #define __CLANG_LIMITS_H /* The system's limits.h may, in turn, try to #include_next GCC's limits.h. Avert this #include_next madness. */ #if defined __GNUC__ && !defined _GCC_LIMITS_H_ #define _GCC_LIMITS_H_ #endif /* System headers include a number of constants from POSIX in . Include it if we're hosted. */ #if __STDC_HOSTED__ && __has_include_next() #include_next #endif /* Many system headers try to "help us out" by defining these. No really, we know how big each datatype is. */ #undef SCHAR_MIN #undef SCHAR_MAX #undef UCHAR_MAX #undef SHRT_MIN #undef SHRT_MAX #undef USHRT_MAX #undef INT_MIN #undef INT_MAX #undef UINT_MAX #undef LONG_MIN #undef LONG_MAX #undef ULONG_MAX #undef CHAR_BIT #undef CHAR_MIN #undef CHAR_MAX /* C90/99 5.2.4.2.1 */ #define SCHAR_MAX __SCHAR_MAX__ #define SHRT_MAX __SHRT_MAX__ #define INT_MAX __INT_MAX__ #define LONG_MAX __LONG_MAX__ #define SCHAR_MIN (-__SCHAR_MAX__-1) #define SHRT_MIN (-__SHRT_MAX__ -1) #define INT_MIN (-__INT_MAX__ -1) #define LONG_MIN (-__LONG_MAX__ -1L) #define UCHAR_MAX (__SCHAR_MAX__*2 +1) #define USHRT_MAX (__SHRT_MAX__ *2 +1) #define UINT_MAX (__INT_MAX__ *2U +1U) #define ULONG_MAX (__LONG_MAX__ *2UL+1UL) #ifndef MB_LEN_MAX #define MB_LEN_MAX 1 #endif #define CHAR_BIT __CHAR_BIT__ #ifdef __CHAR_UNSIGNED__ /* -funsigned-char */ #define CHAR_MIN 0 #define CHAR_MAX UCHAR_MAX #else #define CHAR_MIN SCHAR_MIN #define CHAR_MAX __SCHAR_MAX__ #endif /* C99 5.2.4.2.1: Added long long. C++11 18.3.3.2: same contents as the Standard C Library header . */ #if __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L #undef LLONG_MIN #undef LLONG_MAX #undef ULLONG_MAX #define LLONG_MAX __LONG_LONG_MAX__ #define LLONG_MIN (-__LONG_LONG_MAX__-1LL) #define ULLONG_MAX (__LONG_LONG_MAX__*2ULL+1ULL) #endif /* LONG_LONG_MIN/LONG_LONG_MAX/ULONG_LONG_MAX are a GNU extension. It's too bad that we don't have something like #pragma poison that could be used to deprecate a macro - the code should just use LLONG_MAX and friends. */ #if defined(__GNU_LIBRARY__) ? defined(__USE_GNU) : !defined(__STRICT_ANSI__) #undef LONG_LONG_MIN #undef LONG_LONG_MAX #undef ULONG_LONG_MAX #define LONG_LONG_MAX __LONG_LONG_MAX__ #define LONG_LONG_MIN (-__LONG_LONG_MAX__-1LL) #define ULONG_LONG_MAX (__LONG_LONG_MAX__*2ULL+1ULL) #endif #endif /* __CLANG_LIMITS_H */ bpftrace-0.23.2/src/resources/stdarg.h000066400000000000000000000037501477746507000176500ustar00rootroot00000000000000/*===---- stdarg.h - Variable argument handling ----------------------------=== * * Copyright (c) 2008 Eli Friedman * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * *===-----------------------------------------------------------------------=== */ #ifndef __STDARG_H #define __STDARG_H #ifndef _VA_LIST typedef __builtin_va_list va_list; #define _VA_LIST #endif #define va_start(ap, param) __builtin_va_start(ap, param) #define va_end(ap) __builtin_va_end(ap) #define va_arg(ap, type) __builtin_va_arg(ap, type) /* GCC always defines __va_copy, but does not define va_copy unless in c99 mode * or -ansi is not specified, since it was not part of C90. */ #define __va_copy(d,s) __builtin_va_copy(d,s) #if __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L || !defined(__STRICT_ANSI__) #define va_copy(dest, src) __builtin_va_copy(dest, src) #endif #ifndef __GNUC_VA_LIST #define __GNUC_VA_LIST 1 typedef __builtin_va_list __gnuc_va_list; #endif #endif /* __STDARG_H */ bpftrace-0.23.2/src/resources/stdbool.h000066400000000000000000000017611477746507000200320ustar00rootroot00000000000000/*===---- stdbool.h - Standard header for booleans -------------------------=== * * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. * See https://llvm.org/LICENSE.txt for license information. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception * *===-----------------------------------------------------------------------=== */ #ifndef __STDBOOL_H #define __STDBOOL_H #define __bool_true_false_are_defined 1 #if __STDC_VERSION__ > 201710L /* FIXME: We should be issuing a deprecation warning here, but cannot yet due * to system headers which include this header file unconditionally. */ #elif !defined(__cplusplus) #define bool _Bool #define true 1 #define false 0 #elif defined(__GNUC__) && !defined(__STRICT_ANSI__) /* Define _Bool as a GNU extension. */ #define _Bool bool #if __cplusplus < 201103L /* For C++98, define bool, false, true as a GNU extension. */ #define bool bool #define false false #define true true #endif #endif #endif /* __STDBOOL_H */ bpftrace-0.23.2/src/resources/stddef.h000066400000000000000000000106221477746507000176310ustar00rootroot00000000000000/*===---- stddef.h - Basic type definitions --------------------------------=== * * Copyright (c) 2008 Eli Friedman * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * *===-----------------------------------------------------------------------=== */ #if !defined(__STDDEF_H) || defined(__need_ptrdiff_t) || \ defined(__need_size_t) || defined(__need_wchar_t) || \ defined(__need_NULL) || defined(__need_wint_t) #if !defined(__need_ptrdiff_t) && !defined(__need_size_t) && \ !defined(__need_wchar_t) && !defined(__need_NULL) && \ !defined(__need_wint_t) /* Always define miscellaneous pieces when modules are available. */ #if !__has_feature(modules) #define __STDDEF_H #endif #define __need_ptrdiff_t #define __need_size_t #define __need_wchar_t #define __need_NULL #define __need_STDDEF_H_misc /* __need_wint_t is intentionally not defined here. */ #endif #if defined(__need_ptrdiff_t) #if !defined(_PTRDIFF_T) || __has_feature(modules) /* Always define ptrdiff_t when modules are available. */ #if !__has_feature(modules) #define _PTRDIFF_T #endif typedef __PTRDIFF_TYPE__ ptrdiff_t; #endif #undef __need_ptrdiff_t #endif /* defined(__need_ptrdiff_t) */ #if defined(__need_size_t) #if !defined(_SIZE_T) || __has_feature(modules) /* Always define size_t when modules are available. */ #if !__has_feature(modules) #define _SIZE_T #endif typedef __SIZE_TYPE__ size_t; #endif #undef __need_size_t #endif /*defined(__need_size_t) */ #if defined(__need_STDDEF_H_misc) /* ISO9899:2011 7.20 (C11 Annex K): Define rsize_t if __STDC_WANT_LIB_EXT1__ is * enabled. */ #if (defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ >= 1 && \ !defined(_RSIZE_T)) || __has_feature(modules) /* Always define rsize_t when modules are available. */ #if !__has_feature(modules) #define _RSIZE_T #endif typedef __SIZE_TYPE__ rsize_t; #endif #endif /* defined(__need_STDDEF_H_misc) */ #if defined(__need_wchar_t) #ifndef __cplusplus /* Always define wchar_t when modules are available. */ #if !defined(_WCHAR_T) || __has_feature(modules) #if !__has_feature(modules) #define _WCHAR_T #if defined(_MSC_EXTENSIONS) #define _WCHAR_T_DEFINED #endif #endif typedef __WCHAR_TYPE__ wchar_t; #endif #endif #undef __need_wchar_t #endif /* defined(__need_wchar_t) */ #if defined(__need_NULL) #undef NULL #ifdef __cplusplus # if !defined(__MINGW32__) && !defined(_MSC_VER) # define NULL __null # else # define NULL 0 # endif #else # define NULL ((void*)0) #endif #ifdef __cplusplus #if defined(_MSC_EXTENSIONS) && defined(_NATIVE_NULLPTR_SUPPORTED) namespace std { typedef decltype(nullptr) nullptr_t; } using ::std::nullptr_t; #endif #endif #undef __need_NULL #endif /* defined(__need_NULL) */ #if defined(__need_STDDEF_H_misc) #if __STDC_VERSION__ >= 201112L || __cplusplus >= 201103L #include "__stddef_max_align_t.h" #endif #define offsetof(t, d) __builtin_offsetof(t, d) #undef __need_STDDEF_H_misc #endif /* defined(__need_STDDEF_H_misc) */ /* Some C libraries expect to see a wint_t here. Others (notably MinGW) will use __WINT_TYPE__ directly; accommodate both by requiring __need_wint_t */ #if defined(__need_wint_t) /* Always define wint_t when modules are available. */ #if !defined(_WINT_T) || __has_feature(modules) #if !__has_feature(modules) #define _WINT_T #endif typedef __WINT_TYPE__ wint_t; #endif #undef __need_wint_t #endif /* __need_wint_t */ #endif bpftrace-0.23.2/src/resources/stdint.h000066400000000000000000000555371477746507000177030ustar00rootroot00000000000000/*===---- stdint.h - Standard header for sized integer types --------------===*\ * * Copyright (c) 2009 Chris Lattner * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * \*===----------------------------------------------------------------------===*/ #ifndef __CLANG_STDINT_H #define __CLANG_STDINT_H /* If we're hosted, fall back to the system's stdint.h, which might have * additional definitions. */ #if __STDC_HOSTED__ && __has_include_next() // C99 7.18.3 Limits of other integer types // // Footnote 219, 220: C++ implementations should define these macros only when // __STDC_LIMIT_MACROS is defined before is included. // // Footnote 222: C++ implementations should define these macros only when // __STDC_CONSTANT_MACROS is defined before is included. // // C++11 [cstdint.syn]p2: // // The macros defined by are provided unconditionally. In particular, // the symbols __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS (mentioned in // footnotes 219, 220, and 222 in the C standard) play no role in C++. // // C11 removed the problematic footnotes. // // Work around this inconsistency by always defining those macros in C++ mode, // so that a C library implementation which follows the C99 standard can be // used in C++. # ifdef __cplusplus # if !defined(__STDC_LIMIT_MACROS) # define __STDC_LIMIT_MACROS # define __STDC_LIMIT_MACROS_DEFINED_BY_CLANG # endif # if !defined(__STDC_CONSTANT_MACROS) # define __STDC_CONSTANT_MACROS # define __STDC_CONSTANT_MACROS_DEFINED_BY_CLANG # endif # endif # include_next # ifdef __STDC_LIMIT_MACROS_DEFINED_BY_CLANG # undef __STDC_LIMIT_MACROS # undef __STDC_LIMIT_MACROS_DEFINED_BY_CLANG # endif # ifdef __STDC_CONSTANT_MACROS_DEFINED_BY_CLANG # undef __STDC_CONSTANT_MACROS # undef __STDC_CONSTANT_MACROS_DEFINED_BY_CLANG # endif #else /* C99 7.18.1.1 Exact-width integer types. * C99 7.18.1.2 Minimum-width integer types. * C99 7.18.1.3 Fastest minimum-width integer types. * * The standard requires that exact-width type be defined for 8-, 16-, 32-, and * 64-bit types if they are implemented. Other exact width types are optional. * This implementation defines an exact-width types for every integer width * that is represented in the standard integer types. * * The standard also requires minimum-width types be defined for 8-, 16-, 32-, * and 64-bit widths regardless of whether there are corresponding exact-width * types. * * To accommodate targets that are missing types that are exactly 8, 16, 32, or * 64 bits wide, this implementation takes an approach of cascading * redefinitions, redefining __int_leastN_t to successively smaller exact-width * types. It is therefore important that the types are defined in order of * descending widths. * * We currently assume that the minimum-width types and the fastest * minimum-width types are the same. This is allowed by the standard, but is * suboptimal. * * In violation of the standard, some targets do not implement a type that is * wide enough to represent all of the required widths (8-, 16-, 32-, 64-bit). * To accommodate these targets, a required minimum-width type is only * defined if there exists an exact-width type of equal or greater width. */ #ifdef __INT64_TYPE__ # ifndef __int8_t_defined /* glibc sys/types.h also defines int64_t*/ typedef __INT64_TYPE__ int64_t; # endif /* __int8_t_defined */ typedef __UINT64_TYPE__ uint64_t; # define __int_least64_t int64_t # define __uint_least64_t uint64_t # define __int_least32_t int64_t # define __uint_least32_t uint64_t # define __int_least16_t int64_t # define __uint_least16_t uint64_t # define __int_least8_t int64_t # define __uint_least8_t uint64_t #endif /* __INT64_TYPE__ */ #ifdef __int_least64_t typedef __int_least64_t int_least64_t; typedef __uint_least64_t uint_least64_t; typedef __int_least64_t int_fast64_t; typedef __uint_least64_t uint_fast64_t; #endif /* __int_least64_t */ #ifdef __INT56_TYPE__ typedef __INT56_TYPE__ int56_t; typedef __UINT56_TYPE__ uint56_t; typedef int56_t int_least56_t; typedef uint56_t uint_least56_t; typedef int56_t int_fast56_t; typedef uint56_t uint_fast56_t; # define __int_least32_t int56_t # define __uint_least32_t uint56_t # define __int_least16_t int56_t # define __uint_least16_t uint56_t # define __int_least8_t int56_t # define __uint_least8_t uint56_t #endif /* __INT56_TYPE__ */ #ifdef __INT48_TYPE__ typedef __INT48_TYPE__ int48_t; typedef __UINT48_TYPE__ uint48_t; typedef int48_t int_least48_t; typedef uint48_t uint_least48_t; typedef int48_t int_fast48_t; typedef uint48_t uint_fast48_t; # define __int_least32_t int48_t # define __uint_least32_t uint48_t # define __int_least16_t int48_t # define __uint_least16_t uint48_t # define __int_least8_t int48_t # define __uint_least8_t uint48_t #endif /* __INT48_TYPE__ */ #ifdef __INT40_TYPE__ typedef __INT40_TYPE__ int40_t; typedef __UINT40_TYPE__ uint40_t; typedef int40_t int_least40_t; typedef uint40_t uint_least40_t; typedef int40_t int_fast40_t; typedef uint40_t uint_fast40_t; # define __int_least32_t int40_t # define __uint_least32_t uint40_t # define __int_least16_t int40_t # define __uint_least16_t uint40_t # define __int_least8_t int40_t # define __uint_least8_t uint40_t #endif /* __INT40_TYPE__ */ #ifdef __INT32_TYPE__ # ifndef __int8_t_defined /* glibc sys/types.h also defines int32_t*/ typedef __INT32_TYPE__ int32_t; # endif /* __int8_t_defined */ # ifndef __uint32_t_defined /* more glibc compatibility */ # define __uint32_t_defined typedef __UINT32_TYPE__ uint32_t; # endif /* __uint32_t_defined */ # define __int_least32_t int32_t # define __uint_least32_t uint32_t # define __int_least16_t int32_t # define __uint_least16_t uint32_t # define __int_least8_t int32_t # define __uint_least8_t uint32_t #endif /* __INT32_TYPE__ */ #ifdef __int_least32_t typedef __int_least32_t int_least32_t; typedef __uint_least32_t uint_least32_t; typedef __int_least32_t int_fast32_t; typedef __uint_least32_t uint_fast32_t; #endif /* __int_least32_t */ #ifdef __INT24_TYPE__ typedef __INT24_TYPE__ int24_t; typedef __UINT24_TYPE__ uint24_t; typedef int24_t int_least24_t; typedef uint24_t uint_least24_t; typedef int24_t int_fast24_t; typedef uint24_t uint_fast24_t; # define __int_least16_t int24_t # define __uint_least16_t uint24_t # define __int_least8_t int24_t # define __uint_least8_t uint24_t #endif /* __INT24_TYPE__ */ #ifdef __INT16_TYPE__ #ifndef __int8_t_defined /* glibc sys/types.h also defines int16_t*/ typedef __INT16_TYPE__ int16_t; #endif /* __int8_t_defined */ typedef __UINT16_TYPE__ uint16_t; # define __int_least16_t int16_t # define __uint_least16_t uint16_t # define __int_least8_t int16_t # define __uint_least8_t uint16_t #endif /* __INT16_TYPE__ */ #ifdef __int_least16_t typedef __int_least16_t int_least16_t; typedef __uint_least16_t uint_least16_t; typedef __int_least16_t int_fast16_t; typedef __uint_least16_t uint_fast16_t; #endif /* __int_least16_t */ #ifdef __INT8_TYPE__ #ifndef __int8_t_defined /* glibc sys/types.h also defines int8_t*/ typedef __INT8_TYPE__ int8_t; #endif /* __int8_t_defined */ typedef __UINT8_TYPE__ uint8_t; # define __int_least8_t int8_t # define __uint_least8_t uint8_t #endif /* __INT8_TYPE__ */ #ifdef __int_least8_t typedef __int_least8_t int_least8_t; typedef __uint_least8_t uint_least8_t; typedef __int_least8_t int_fast8_t; typedef __uint_least8_t uint_fast8_t; #endif /* __int_least8_t */ /* prevent glibc sys/types.h from defining conflicting types */ #ifndef __int8_t_defined # define __int8_t_defined #endif /* __int8_t_defined */ /* C99 7.18.1.4 Integer types capable of holding object pointers. */ #define __stdint_join3(a,b,c) a ## b ## c #ifndef _INTPTR_T #ifndef __intptr_t_defined typedef __INTPTR_TYPE__ intptr_t; #define __intptr_t_defined #define _INTPTR_T #endif #endif #ifndef _UINTPTR_T typedef __UINTPTR_TYPE__ uintptr_t; #define _UINTPTR_T #endif /* C99 7.18.1.5 Greatest-width integer types. */ typedef __INTMAX_TYPE__ intmax_t; typedef __UINTMAX_TYPE__ uintmax_t; /* C99 7.18.4 Macros for minimum-width integer constants. * * The standard requires that integer constant macros be defined for all the * minimum-width types defined above. As 8-, 16-, 32-, and 64-bit minimum-width * types are required, the corresponding integer constant macros are defined * here. This implementation also defines minimum-width types for every other * integer width that the target implements, so corresponding macros are * defined below, too. * * These macros are defined using the same successive-shrinking approach as * the type definitions above. It is likewise important that macros are defined * in order of descending width. * * Note that C++ should not check __STDC_CONSTANT_MACROS here, contrary to the * claims of the C standard (see C++ 18.3.1p2, [cstdint.syn]). */ #define __int_c_join(a, b) a ## b #define __int_c(v, suffix) __int_c_join(v, suffix) #define __uint_c(v, suffix) __int_c_join(v##U, suffix) #ifdef __INT64_TYPE__ # ifdef __INT64_C_SUFFIX__ # define __int64_c_suffix __INT64_C_SUFFIX__ # define __int32_c_suffix __INT64_C_SUFFIX__ # define __int16_c_suffix __INT64_C_SUFFIX__ # define __int8_c_suffix __INT64_C_SUFFIX__ # else # undef __int64_c_suffix # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT64_C_SUFFIX__ */ #endif /* __INT64_TYPE__ */ #ifdef __int_least64_t # ifdef __int64_c_suffix # define INT64_C(v) __int_c(v, __int64_c_suffix) # define UINT64_C(v) __uint_c(v, __int64_c_suffix) # else # define INT64_C(v) v # define UINT64_C(v) v ## U # endif /* __int64_c_suffix */ #endif /* __int_least64_t */ #ifdef __INT56_TYPE__ # ifdef __INT56_C_SUFFIX__ # define INT56_C(v) __int_c(v, __INT56_C_SUFFIX__) # define UINT56_C(v) __uint_c(v, __INT56_C_SUFFIX__) # define __int32_c_suffix __INT56_C_SUFFIX__ # define __int16_c_suffix __INT56_C_SUFFIX__ # define __int8_c_suffix __INT56_C_SUFFIX__ # else # define INT56_C(v) v # define UINT56_C(v) v ## U # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT56_C_SUFFIX__ */ #endif /* __INT56_TYPE__ */ #ifdef __INT48_TYPE__ # ifdef __INT48_C_SUFFIX__ # define INT48_C(v) __int_c(v, __INT48_C_SUFFIX__) # define UINT48_C(v) __uint_c(v, __INT48_C_SUFFIX__) # define __int32_c_suffix __INT48_C_SUFFIX__ # define __int16_c_suffix __INT48_C_SUFFIX__ # define __int8_c_suffix __INT48_C_SUFFIX__ # else # define INT48_C(v) v # define UINT48_C(v) v ## U # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT48_C_SUFFIX__ */ #endif /* __INT48_TYPE__ */ #ifdef __INT40_TYPE__ # ifdef __INT40_C_SUFFIX__ # define INT40_C(v) __int_c(v, __INT40_C_SUFFIX__) # define UINT40_C(v) __uint_c(v, __INT40_C_SUFFIX__) # define __int32_c_suffix __INT40_C_SUFFIX__ # define __int16_c_suffix __INT40_C_SUFFIX__ # define __int8_c_suffix __INT40_C_SUFFIX__ # else # define INT40_C(v) v # define UINT40_C(v) v ## U # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT40_C_SUFFIX__ */ #endif /* __INT40_TYPE__ */ #ifdef __INT32_TYPE__ # ifdef __INT32_C_SUFFIX__ # define __int32_c_suffix __INT32_C_SUFFIX__ # define __int16_c_suffix __INT32_C_SUFFIX__ # define __int8_c_suffix __INT32_C_SUFFIX__ #else # undef __int32_c_suffix # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT32_C_SUFFIX__ */ #endif /* __INT32_TYPE__ */ #ifdef __int_least32_t # ifdef __int32_c_suffix # define INT32_C(v) __int_c(v, __int32_c_suffix) # define UINT32_C(v) __uint_c(v, __int32_c_suffix) # else # define INT32_C(v) v # define UINT32_C(v) v ## U # endif /* __int32_c_suffix */ #endif /* __int_least32_t */ #ifdef __INT24_TYPE__ # ifdef __INT24_C_SUFFIX__ # define INT24_C(v) __int_c(v, __INT24_C_SUFFIX__) # define UINT24_C(v) __uint_c(v, __INT24_C_SUFFIX__) # define __int16_c_suffix __INT24_C_SUFFIX__ # define __int8_c_suffix __INT24_C_SUFFIX__ # else # define INT24_C(v) v # define UINT24_C(v) v ## U # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT24_C_SUFFIX__ */ #endif /* __INT24_TYPE__ */ #ifdef __INT16_TYPE__ # ifdef __INT16_C_SUFFIX__ # define __int16_c_suffix __INT16_C_SUFFIX__ # define __int8_c_suffix __INT16_C_SUFFIX__ #else # undef __int16_c_suffix # undef __int8_c_suffix # endif /* __INT16_C_SUFFIX__ */ #endif /* __INT16_TYPE__ */ #ifdef __int_least16_t # ifdef __int16_c_suffix # define INT16_C(v) __int_c(v, __int16_c_suffix) # define UINT16_C(v) __uint_c(v, __int16_c_suffix) # else # define INT16_C(v) v # define UINT16_C(v) v ## U # endif /* __int16_c_suffix */ #endif /* __int_least16_t */ #ifdef __INT8_TYPE__ # ifdef __INT8_C_SUFFIX__ # define __int8_c_suffix __INT8_C_SUFFIX__ #else # undef __int8_c_suffix # endif /* __INT8_C_SUFFIX__ */ #endif /* __INT8_TYPE__ */ #ifdef __int_least8_t # ifdef __int8_c_suffix # define INT8_C(v) __int_c(v, __int8_c_suffix) # define UINT8_C(v) __uint_c(v, __int8_c_suffix) # else # define INT8_C(v) v # define UINT8_C(v) v ## U # endif /* __int8_c_suffix */ #endif /* __int_least8_t */ /* C99 7.18.2.1 Limits of exact-width integer types. * C99 7.18.2.2 Limits of minimum-width integer types. * C99 7.18.2.3 Limits of fastest minimum-width integer types. * * The presence of limit macros are completely optional in C99. This * implementation defines limits for all of the types (exact- and * minimum-width) that it defines above, using the limits of the minimum-width * type for any types that do not have exact-width representations. * * As in the type definitions, this section takes an approach of * successive-shrinking to determine which limits to use for the standard (8, * 16, 32, 64) bit widths when they don't have exact representations. It is * therefore important that the definitions be kept in order of descending * widths. * * Note that C++ should not check __STDC_LIMIT_MACROS here, contrary to the * claims of the C standard (see C++ 18.3.1p2, [cstdint.syn]). */ #ifdef __INT64_TYPE__ # define INT64_MAX INT64_C( 9223372036854775807) # define INT64_MIN (-INT64_C( 9223372036854775807)-1) # define UINT64_MAX UINT64_C(18446744073709551615) # define __INT_LEAST64_MIN INT64_MIN # define __INT_LEAST64_MAX INT64_MAX # define __UINT_LEAST64_MAX UINT64_MAX # define __INT_LEAST32_MIN INT64_MIN # define __INT_LEAST32_MAX INT64_MAX # define __UINT_LEAST32_MAX UINT64_MAX # define __INT_LEAST16_MIN INT64_MIN # define __INT_LEAST16_MAX INT64_MAX # define __UINT_LEAST16_MAX UINT64_MAX # define __INT_LEAST8_MIN INT64_MIN # define __INT_LEAST8_MAX INT64_MAX # define __UINT_LEAST8_MAX UINT64_MAX #endif /* __INT64_TYPE__ */ #ifdef __INT_LEAST64_MIN # define INT_LEAST64_MIN __INT_LEAST64_MIN # define INT_LEAST64_MAX __INT_LEAST64_MAX # define UINT_LEAST64_MAX __UINT_LEAST64_MAX # define INT_FAST64_MIN __INT_LEAST64_MIN # define INT_FAST64_MAX __INT_LEAST64_MAX # define UINT_FAST64_MAX __UINT_LEAST64_MAX #endif /* __INT_LEAST64_MIN */ #ifdef __INT56_TYPE__ # define INT56_MAX INT56_C(36028797018963967) # define INT56_MIN (-INT56_C(36028797018963967)-1) # define UINT56_MAX UINT56_C(72057594037927935) # define INT_LEAST56_MIN INT56_MIN # define INT_LEAST56_MAX INT56_MAX # define UINT_LEAST56_MAX UINT56_MAX # define INT_FAST56_MIN INT56_MIN # define INT_FAST56_MAX INT56_MAX # define UINT_FAST56_MAX UINT56_MAX # define __INT_LEAST32_MIN INT56_MIN # define __INT_LEAST32_MAX INT56_MAX # define __UINT_LEAST32_MAX UINT56_MAX # define __INT_LEAST16_MIN INT56_MIN # define __INT_LEAST16_MAX INT56_MAX # define __UINT_LEAST16_MAX UINT56_MAX # define __INT_LEAST8_MIN INT56_MIN # define __INT_LEAST8_MAX INT56_MAX # define __UINT_LEAST8_MAX UINT56_MAX #endif /* __INT56_TYPE__ */ #ifdef __INT48_TYPE__ # define INT48_MAX INT48_C(140737488355327) # define INT48_MIN (-INT48_C(140737488355327)-1) # define UINT48_MAX UINT48_C(281474976710655) # define INT_LEAST48_MIN INT48_MIN # define INT_LEAST48_MAX INT48_MAX # define UINT_LEAST48_MAX UINT48_MAX # define INT_FAST48_MIN INT48_MIN # define INT_FAST48_MAX INT48_MAX # define UINT_FAST48_MAX UINT48_MAX # define __INT_LEAST32_MIN INT48_MIN # define __INT_LEAST32_MAX INT48_MAX # define __UINT_LEAST32_MAX UINT48_MAX # define __INT_LEAST16_MIN INT48_MIN # define __INT_LEAST16_MAX INT48_MAX # define __UINT_LEAST16_MAX UINT48_MAX # define __INT_LEAST8_MIN INT48_MIN # define __INT_LEAST8_MAX INT48_MAX # define __UINT_LEAST8_MAX UINT48_MAX #endif /* __INT48_TYPE__ */ #ifdef __INT40_TYPE__ # define INT40_MAX INT40_C(549755813887) # define INT40_MIN (-INT40_C(549755813887)-1) # define UINT40_MAX UINT40_C(1099511627775) # define INT_LEAST40_MIN INT40_MIN # define INT_LEAST40_MAX INT40_MAX # define UINT_LEAST40_MAX UINT40_MAX # define INT_FAST40_MIN INT40_MIN # define INT_FAST40_MAX INT40_MAX # define UINT_FAST40_MAX UINT40_MAX # define __INT_LEAST32_MIN INT40_MIN # define __INT_LEAST32_MAX INT40_MAX # define __UINT_LEAST32_MAX UINT40_MAX # define __INT_LEAST16_MIN INT40_MIN # define __INT_LEAST16_MAX INT40_MAX # define __UINT_LEAST16_MAX UINT40_MAX # define __INT_LEAST8_MIN INT40_MIN # define __INT_LEAST8_MAX INT40_MAX # define __UINT_LEAST8_MAX UINT40_MAX #endif /* __INT40_TYPE__ */ #ifdef __INT32_TYPE__ # define INT32_MAX INT32_C(2147483647) # define INT32_MIN (-INT32_C(2147483647)-1) # define UINT32_MAX UINT32_C(4294967295) # define __INT_LEAST32_MIN INT32_MIN # define __INT_LEAST32_MAX INT32_MAX # define __UINT_LEAST32_MAX UINT32_MAX # define __INT_LEAST16_MIN INT32_MIN # define __INT_LEAST16_MAX INT32_MAX # define __UINT_LEAST16_MAX UINT32_MAX # define __INT_LEAST8_MIN INT32_MIN # define __INT_LEAST8_MAX INT32_MAX # define __UINT_LEAST8_MAX UINT32_MAX #endif /* __INT32_TYPE__ */ #ifdef __INT_LEAST32_MIN # define INT_LEAST32_MIN __INT_LEAST32_MIN # define INT_LEAST32_MAX __INT_LEAST32_MAX # define UINT_LEAST32_MAX __UINT_LEAST32_MAX # define INT_FAST32_MIN __INT_LEAST32_MIN # define INT_FAST32_MAX __INT_LEAST32_MAX # define UINT_FAST32_MAX __UINT_LEAST32_MAX #endif /* __INT_LEAST32_MIN */ #ifdef __INT24_TYPE__ # define INT24_MAX INT24_C(8388607) # define INT24_MIN (-INT24_C(8388607)-1) # define UINT24_MAX UINT24_C(16777215) # define INT_LEAST24_MIN INT24_MIN # define INT_LEAST24_MAX INT24_MAX # define UINT_LEAST24_MAX UINT24_MAX # define INT_FAST24_MIN INT24_MIN # define INT_FAST24_MAX INT24_MAX # define UINT_FAST24_MAX UINT24_MAX # define __INT_LEAST16_MIN INT24_MIN # define __INT_LEAST16_MAX INT24_MAX # define __UINT_LEAST16_MAX UINT24_MAX # define __INT_LEAST8_MIN INT24_MIN # define __INT_LEAST8_MAX INT24_MAX # define __UINT_LEAST8_MAX UINT24_MAX #endif /* __INT24_TYPE__ */ #ifdef __INT16_TYPE__ #define INT16_MAX INT16_C(32767) #define INT16_MIN (-INT16_C(32767)-1) #define UINT16_MAX UINT16_C(65535) # define __INT_LEAST16_MIN INT16_MIN # define __INT_LEAST16_MAX INT16_MAX # define __UINT_LEAST16_MAX UINT16_MAX # define __INT_LEAST8_MIN INT16_MIN # define __INT_LEAST8_MAX INT16_MAX # define __UINT_LEAST8_MAX UINT16_MAX #endif /* __INT16_TYPE__ */ #ifdef __INT_LEAST16_MIN # define INT_LEAST16_MIN __INT_LEAST16_MIN # define INT_LEAST16_MAX __INT_LEAST16_MAX # define UINT_LEAST16_MAX __UINT_LEAST16_MAX # define INT_FAST16_MIN __INT_LEAST16_MIN # define INT_FAST16_MAX __INT_LEAST16_MAX # define UINT_FAST16_MAX __UINT_LEAST16_MAX #endif /* __INT_LEAST16_MIN */ #ifdef __INT8_TYPE__ # define INT8_MAX INT8_C(127) # define INT8_MIN (-INT8_C(127)-1) # define UINT8_MAX UINT8_C(255) # define __INT_LEAST8_MIN INT8_MIN # define __INT_LEAST8_MAX INT8_MAX # define __UINT_LEAST8_MAX UINT8_MAX #endif /* __INT8_TYPE__ */ #ifdef __INT_LEAST8_MIN # define INT_LEAST8_MIN __INT_LEAST8_MIN # define INT_LEAST8_MAX __INT_LEAST8_MAX # define UINT_LEAST8_MAX __UINT_LEAST8_MAX # define INT_FAST8_MIN __INT_LEAST8_MIN # define INT_FAST8_MAX __INT_LEAST8_MAX # define UINT_FAST8_MAX __UINT_LEAST8_MAX #endif /* __INT_LEAST8_MIN */ /* Some utility macros */ #define __INTN_MIN(n) __stdint_join3( INT, n, _MIN) #define __INTN_MAX(n) __stdint_join3( INT, n, _MAX) #define __UINTN_MAX(n) __stdint_join3(UINT, n, _MAX) #define __INTN_C(n, v) __stdint_join3( INT, n, _C(v)) #define __UINTN_C(n, v) __stdint_join3(UINT, n, _C(v)) /* C99 7.18.2.4 Limits of integer types capable of holding object pointers. */ /* C99 7.18.3 Limits of other integer types. */ #define INTPTR_MIN (-__INTPTR_MAX__-1) #define INTPTR_MAX __INTPTR_MAX__ #define UINTPTR_MAX __UINTPTR_MAX__ #define PTRDIFF_MIN (-__PTRDIFF_MAX__-1) #define PTRDIFF_MAX __PTRDIFF_MAX__ #define SIZE_MAX __SIZE_MAX__ /* ISO9899:2011 7.20 (C11 Annex K): Define RSIZE_MAX if __STDC_WANT_LIB_EXT1__ * is enabled. */ #if defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ >= 1 #define RSIZE_MAX (SIZE_MAX >> 1) #endif /* C99 7.18.2.5 Limits of greatest-width integer types. */ #define INTMAX_MIN (-__INTMAX_MAX__-1) #define INTMAX_MAX __INTMAX_MAX__ #define UINTMAX_MAX __UINTMAX_MAX__ /* C99 7.18.3 Limits of other integer types. */ #define SIG_ATOMIC_MIN __INTN_MIN(__SIG_ATOMIC_WIDTH__) #define SIG_ATOMIC_MAX __INTN_MAX(__SIG_ATOMIC_WIDTH__) #ifdef __WINT_UNSIGNED__ # define WINT_MIN __UINTN_C(__WINT_WIDTH__, 0) # define WINT_MAX __UINTN_MAX(__WINT_WIDTH__) #else # define WINT_MIN __INTN_MIN(__WINT_WIDTH__) # define WINT_MAX __INTN_MAX(__WINT_WIDTH__) #endif #ifndef WCHAR_MAX # define WCHAR_MAX __WCHAR_MAX__ #endif #ifndef WCHAR_MIN # if __WCHAR_MAX__ == __INTN_MAX(__WCHAR_WIDTH__) # define WCHAR_MIN __INTN_MIN(__WCHAR_WIDTH__) # else # define WCHAR_MIN __UINTN_C(__WCHAR_WIDTH__, 0) # endif #endif /* 7.18.4.2 Macros for greatest-width integer constants. */ #define INTMAX_C(v) __int_c(v, __INTMAX_C_SUFFIX__) #define UINTMAX_C(v) __int_c(v, __UINTMAX_C_SUFFIX__) #endif /* __STDC_HOSTED__ */ #endif /* __CLANG_STDINT_H */ bpftrace-0.23.2/src/run_bpftrace.cpp000066400000000000000000000035431477746507000173570ustar00rootroot00000000000000#include #include "log.h" #include "run_bpftrace.h" using namespace bpftrace; static const char *libbpf_print_level_string(enum libbpf_print_level level) { switch (level) { case LIBBPF_WARN: return "WARN"; case LIBBPF_INFO: return "INFO"; default: return "DEBUG"; } } int libbpf_print(enum libbpf_print_level level, const char *msg, va_list ap) { if (bt_debug.find(DebugStage::Libbpf) == bt_debug.end()) return 0; printf("[%s] ", libbpf_print_level_string(level)); return vprintf(msg, ap); } void check_is_root() { if (geteuid() != 0) { LOG(ERROR) << "bpftrace currently only supports running as the root user."; exit(1); } } int run_bpftrace(BPFtrace &bpftrace, BpfBytecode &bytecode) { int err; // Signal handler that lets us know an exit signal was received. struct sigaction act = {}; act.sa_handler = [](int) { BPFtrace::exitsig_recv = true; }; sigaction(SIGINT, &act, nullptr); sigaction(SIGTERM, &act, nullptr); // Signal handler that prints all maps when SIGUSR1 was received. act.sa_handler = [](int) { BPFtrace::sigusr1_recv = true; }; sigaction(SIGUSR1, &act, nullptr); err = bpftrace.run(std::move(bytecode)); if (err) return err; // We are now post-processing. If we receive another SIGINT, // handle it normally (exit) act.sa_handler = SIG_DFL; sigaction(SIGINT, &act, nullptr); std::cout << "\n\n"; // Print maps if needed (true by default). if (bpftrace.config_.get(ConfigKeyBool::print_maps_on_exit)) err = bpftrace.print_maps(); if (bpftrace.child_) { auto val = 0; if ((val = bpftrace.child_->term_signal()) > -1) LOG(V1) << "Child terminated by signal: " << val; if ((val = bpftrace.child_->exit_code()) > -1) LOG(V1) << "Child exited with code: " << val; } if (err) return err; return BPFtrace::exit_code; } bpftrace-0.23.2/src/run_bpftrace.h000066400000000000000000000003331477746507000170160ustar00rootroot00000000000000#pragma once #include "bpftrace.h" int libbpf_print(enum libbpf_print_level level, const char *msg, va_list ap); void check_is_root(); int run_bpftrace(bpftrace::BPFtrace &bpftrace, bpftrace::BpfBytecode &bytecode); bpftrace-0.23.2/src/scopeguard.h000066400000000000000000000012451477746507000165030ustar00rootroot00000000000000#pragma once #include // Need two levels of indirection here for __LINE__ to correctly expand #define _CONCAT2(a, b) a##b #define _CONCAT(a, b) _CONCAT2(a, b) #define _ANON_VAR(str) _CONCAT(str, __LINE__) namespace bpftrace { enum class ScopeGuardExit {}; class ScopeGuard { public: explicit ScopeGuard(std::function fn) { fn_ = fn; } ~ScopeGuard() { if (fn_) { fn_(); } } private: std::function fn_; }; inline ScopeGuard operator+(ScopeGuardExit, std::function fn) { return ScopeGuard(fn); } } // namespace bpftrace #define SCOPE_EXIT auto _ANON_VAR(SCOPE_EXIT_STATE) = ScopeGuardExit() + [&]() bpftrace-0.23.2/src/struct.cpp000066400000000000000000000155361477746507000162360ustar00rootroot00000000000000#include "struct.h" #include #include #include "log.h" #include "utils.h" namespace bpftrace { const size_t BIFTIELD_BIT_WIDTH_MAX = sizeof(uint64_t) * 8; Bitfield::Bitfield(size_t bit_offset, size_t bit_width) { // To handle bitfields, we need to give codegen 3 additional pieces // of information: `read_bytes`, `access_rshift`, and `mask`. // // `read_bytes` tells codegen how many bytes to read starting at // `Field::offset`. This information is necessary because we can't always // issue, for example, a 1 byte read, as the bitfield could be the last 4 bits // of the struct. Reading past the end of the struct could cause a page fault. // Therefore, we compute the minimum number of bytes necessary to fully read // the bitfield. This will always keep the read within the bounds of the // struct. // // `access_rshift` tells codegen how much to shift the masked value so that // the LSB of the bitfield is the LSB of the interpreted integer. // // `mask` tells codegen how to mask out the surrounding bitfields. if (bit_width > BIFTIELD_BIT_WIDTH_MAX) { LOG(WARNING) << "bitfield bitwidth " << bit_width << "is not supported." << " Use bitwidth " << BIFTIELD_BIT_WIDTH_MAX; bit_width = BIFTIELD_BIT_WIDTH_MAX; } if (bit_width == BIFTIELD_BIT_WIDTH_MAX) mask = std::numeric_limits::max(); else mask = (1ULL << bit_width) - 1; // Round up to nearest byte read_bytes = (bit_offset + bit_width + 7) / 8; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ access_rshift = bit_offset; #else access_rshift = (read_bytes * 8 - bit_offset - bit_width); #endif } bool Bitfield::operator==(const Bitfield &other) const { return read_bytes == other.read_bytes && mask == other.mask && access_rshift == other.access_rshift; } bool Bitfield::operator!=(const Bitfield &other) const { return !(*this == other); } // Creates a struct or tuple with the given field types. // If field_names is empty then all fields with be created without names. std::unique_ptr Struct::CreateRecord( const std::vector &fields, const std::vector &field_names) { assert(field_names.empty() || field_names.size() == fields.size()); // See llvm::StructLayout::StructLayout source auto record = std::make_unique(0); ssize_t offset = 0; ssize_t struct_align = 1; for (size_t i = 0; i < fields.size(); i++) { const auto &field = fields[i]; auto align = field.GetInTupleAlignment(); struct_align = std::max(align, struct_align); auto size = field.GetSize(); auto padding = (align - (offset % align)) % align; if (padding) record->padded = true; offset += padding; record->fields.push_back(Field{ .name = field_names.empty() ? "" : std::string{ field_names[i] }, .type = field, .offset = offset, .bitfield = std::nullopt, }); offset += size; } auto padding = (struct_align - (offset % struct_align)) % struct_align; record->size = offset + padding; record->align = struct_align; return record; } std::unique_ptr Struct::CreateTuple( const std::vector &fields) { return CreateRecord(fields, {}); } void Struct::Dump(std::ostream &os) { os << " {" << std::endl; auto pad = [](int size) -> std::string { return "__pad[" + std::to_string(size) + "]"; }; auto prefix = [](int offset, std::ostream &os) -> std::ostream & { os << " " << std::setfill(' ') << std::setw(3) << offset << " | "; return os; }; ssize_t offset = 0; for (const auto &field : fields) { auto delta = field.offset - offset; if (delta) { prefix(offset, os) << pad(delta) << std::endl; } prefix(offset + delta, os) << field.type << std::endl; offset = field.offset + field.type.GetSize(); } os << "} sizeof: [" << size << "]" << std::endl; } bool Struct::HasField(const std::string &name) const { for (auto &field : fields) { if (field.name == name) return true; } return false; } const Field &Struct::GetField(const std::string &name) const { for (auto &field : fields) { if (field.name == name) return field; } throw FatalUserException("struct has no field named " + name); } void Struct::AddField(const std::string &field_name, const SizedType &type, ssize_t offset, const std::optional &bitfield, bool is_data_loc) { if (!HasField(field_name)) fields.push_back(Field{ .name = field_name, .type = type, .offset = offset, .bitfield = bitfield, .is_data_loc = is_data_loc }); } bool Struct::HasFields() const { return !fields.empty(); } void Struct::ClearFields() { fields.clear(); } std::weak_ptr StructManager::Add(const std::string &name, size_t size, bool allow_override) { auto [it, inserted] = struct_map_.insert( { name, std::make_unique(size, allow_override) }); if (!inserted) throw FatalUserException("Type redefinition: type with name \'" + name + "\' already exists"); return it->second; } void StructManager::Add(const std::string &name, Struct &&record) { struct_map_[name] = std::make_shared(std::move(record)); } std::weak_ptr StructManager::Lookup(const std::string &name) const { auto s = struct_map_.find(name); return s != struct_map_.end() ? s->second : std::weak_ptr(); } std::weak_ptr StructManager::LookupOrAdd(const std::string &name, size_t size, bool allow_override) { auto s = struct_map_.insert( { name, std::make_unique(size, allow_override) }); return s.first->second; } bool StructManager::Has(const std::string &name) const { return struct_map_.find(name) != struct_map_.end(); } std::weak_ptr StructManager::AddAnonymousStruct( const std::vector &fields, const std::vector &field_names) { auto t = anonymous_types_.insert(Struct::CreateRecord(fields, field_names)); return *t.first; } std::weak_ptr StructManager::AddTuple( const std::vector &fields) { auto t = anonymous_types_.insert(Struct::CreateTuple(fields)); return *t.first; } size_t StructManager::GetTuplesCnt() const { return anonymous_types_.size(); } const Field *StructManager::GetProbeArg(const ast::Probe &probe, const std::string &arg_name) { auto args = Lookup(probe.args_typename()).lock(); if (!args || !args->HasField(arg_name)) return nullptr; return &args->GetField(arg_name); } } // namespace bpftrace bpftrace-0.23.2/src/struct.h000066400000000000000000000120501477746507000156670ustar00rootroot00000000000000#pragma once #include #include #include #include #include "ast/ast.h" #include "types.h" #include "utils.h" namespace bpftrace { static constexpr auto RETVAL_FIELD_NAME = "$retval"; struct Bitfield { Bitfield(size_t byte_offset, size_t bit_width); Bitfield(size_t read_bytes, size_t access_rshift, uint64_t mask) : read_bytes(read_bytes), access_rshift(access_rshift), mask(mask) { } Bitfield() // necessary for serialization { } bool operator==(const Bitfield &other) const; bool operator!=(const Bitfield &other) const; // Read `read_bytes` bytes starting from this field's offset size_t read_bytes; // Then rshift the resulting value by `access_rshift` to get field value size_t access_rshift; // Then logical AND `mask` to mask out everything but this bitfield uint64_t mask; private: friend class cereal::access; template void serialize(Archive &archive) { archive(read_bytes, access_rshift, mask); } }; struct Field { std::string name; SizedType type; ssize_t offset; std::optional bitfield; // Used for tracepoint __data_loc's // // If true, this field is a 32 bit integer whose value encodes information on // where to find the actual data. The first 2 bytes is the size of the data. // The last 2 bytes is the offset from the start of the tracepoint struct // where the data begins. bool is_data_loc = false; bool operator==(const Field &rhs) const { return name == rhs.name && type == rhs.type && offset == rhs.offset && bitfield == rhs.bitfield && is_data_loc == rhs.is_data_loc; } private: friend class cereal::access; template void serialize(Archive &archive) { archive(name, type, offset, bitfield, is_data_loc); } }; using Fields = std::vector; struct Struct { int size = -1; // in bytes int align = 1; // in bytes, used for tuples only bool padded = false; Fields fields; bool allow_override = true; Struct() = default; explicit Struct(int size, bool allow_override = true) : size(size), allow_override(allow_override) { } bool HasField(const std::string &name) const; const Field &GetField(const std::string &name) const; void AddField(const std::string &field_name, const SizedType &type, ssize_t offset = 0, const std::optional &bitfield = std::nullopt, bool is_data_loc = false); bool HasFields() const; void ClearFields(); static std::unique_ptr CreateRecord( const std::vector &fields, const std::vector &field_names); static std::unique_ptr CreateTuple( const std::vector &fields); void Dump(std::ostream &os); bool operator==(const Struct &rhs) const { return size == rhs.size && align == rhs.align && padded == rhs.padded && fields == rhs.fields; } bool operator!=(const Struct &rhs) const { return !(*this == rhs); } private: friend class cereal::access; template void serialize(Archive &archive) { archive(size, align, padded, fields); } }; std::ostream &operator<<(std::ostream &os, const Fields &t); } // namespace bpftrace namespace std { template <> struct hash { size_t operator()(const bpftrace::Struct &s) const { size_t hash = std::hash()(s.size); for (auto &field : s.fields) bpftrace::hash_combine(hash, field.type); return hash; } }; template <> struct hash> { size_t operator()(const std::shared_ptr &s_ptr) const { return std::hash()(*s_ptr); } }; template <> struct equal_to> { bool operator()(const std::shared_ptr &lhs, const std::shared_ptr &rhs) const { return *lhs == *rhs; } }; } // namespace std namespace bpftrace { class StructManager { public: // struct map manipulation std::weak_ptr Add(const std::string &name, size_t size, bool allow_override = true); void Add(const std::string &name, Struct &&record); std::weak_ptr Lookup(const std::string &name) const; std::weak_ptr LookupOrAdd(const std::string &name, size_t size, bool allow_override = true); bool Has(const std::string &name) const; std::weak_ptr AddAnonymousStruct( const std::vector &fields, const std::vector &field_names); std::weak_ptr AddTuple(const std::vector &fields); size_t GetTuplesCnt() const; // probe args lookup const Field *GetProbeArg(const ast::Probe &probe, const std::string &arg_name); private: std::map> struct_map_; std::unordered_set> anonymous_types_; }; } // namespace bpftrace bpftrace-0.23.2/src/tracefs.cpp000066400000000000000000000011231477746507000163240ustar00rootroot00000000000000#include "tracefs.h" #include namespace bpftrace::tracefs { #define DEBUGFS_TRACEFS "/sys/kernel/debug/tracing" #define TRACEFS "/sys/kernel/tracing" std::string path() { static bool use_debugfs = access(DEBUGFS_TRACEFS, F_OK) == 0; return use_debugfs ? DEBUGFS_TRACEFS : TRACEFS; } std::string path(const std::string &file) { return path() + "/" + file; } std::string event_format_file(const std::string &category, const std::string &event) { return path("events/" + category + "/" + event + "/format"); } } // namespace bpftrace::tracefs bpftrace-0.23.2/src/tracefs.h000066400000000000000000000010321477746507000157700ustar00rootroot00000000000000#pragma once #include namespace bpftrace { namespace tracefs { std::string path(); std::string path(const std::string &file); inline std::string available_events() { return path("available_events"); } inline std::string events() { return path("events"); } inline std::string available_filter_functions() { return path("available_filter_functions"); } std::string event_format_file(const std::string &category, const std::string &event); } // namespace tracefs } // namespace bpftrace bpftrace-0.23.2/src/tracepoint_format_parser.cpp000066400000000000000000000223111477746507000217730ustar00rootroot00000000000000#include #include #include #include #include "ast/ast.h" #include "bpftrace.h" #include "log.h" #include "struct.h" #include "tracefs.h" #include "tracepoint_format_parser.h" namespace bpftrace { std::set TracepointFormatParser::struct_list; bool TracepointFormatParser::parse(ast::ASTContext &ctx, BPFtrace &bpftrace) { ast::Program *program = ctx.root; std::vector probes_with_tracepoint; for (ast::Probe *probe : program->probes) { if (probe->has_ap_of_probetype(ProbeType::tracepoint)) probes_with_tracepoint.push_back(probe); } if (probes_with_tracepoint.empty()) return true; ast::TracepointArgsVisitor n(ctx); if (!bpftrace.has_btf_data()) program->c_definitions += "#include \n"; for (ast::Probe *probe : probes_with_tracepoint) { n.visit(*probe); for (ast::AttachPoint *ap : probe->attach_points) { if (ap->provider == "tracepoint") { std::string &category = ap->target; std::string &event_name = ap->func; std::string format_file_path = tracefs::event_format_file(category, event_name); glob_t glob_result; if (has_wildcard(category) || has_wildcard(event_name)) { // tracepoint wildcard expansion, part 1 of 3. struct definitions. memset(&glob_result, 0, sizeof(glob_result)); int ret = glob(format_file_path.c_str(), 0, nullptr, &glob_result); if (ret != 0) { if (ret == GLOB_NOMATCH) { LOG(ERROR, ap->loc, std::cerr) << "tracepoints not found: " << category << ":" << event_name; // helper message: if (category == "syscall") LOG(ERROR, ap->loc, std::cerr) << "Did you mean syscalls:" << event_name << "?"; LOG(V1) << strerror(errno) << ": " << format_file_path; return false; } else { // unexpected error LOG(ERROR, ap->loc, std::cerr) << std::string(strerror(errno)); return false; } } if (probe->tp_args_structs_level <= 0) { globfree(&glob_result); continue; } for (size_t i = 0; i < glob_result.gl_pathc; ++i) { std::string filename(glob_result.gl_pathv[i]); std::ifstream format_file(filename); const std::string prefix = tracefs::events() + "/"; size_t pos = prefix.length(); std::string real_category = filename.substr( pos, filename.find('/', pos) - pos); pos = prefix.length() + real_category.length() + 1; std::string real_event = filename.substr( pos, filename.length() - std::string("/format").length() - pos); // Check to avoid adding the same struct more than once to // definitions std::string struct_name = get_struct_name(real_category, real_event); if (!TracepointFormatParser::struct_list.count(struct_name)) { program->c_definitions += get_tracepoint_struct( format_file, real_category, real_event, bpftrace); TracepointFormatParser::struct_list.insert(struct_name); } } globfree(&glob_result); } else { // single tracepoint std::ifstream format_file(format_file_path.c_str()); if (format_file.fail()) { // Errno might get clobbered by LOG(). int saved_errno = errno; // 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 (fail && bt_verbose) { // Having the location info isn't really useful here, so no // bpftrace.error LOG(ERROR) << strerror(saved_errno) << ": " << format_file_path; } if (fail) return false; else continue; } if (probe->tp_args_structs_level <= 0) continue; // Check to avoid adding the same struct more than once to definitions std::string struct_name = get_struct_name(category, event_name); if (TracepointFormatParser::struct_list.insert(struct_name).second) program->c_definitions += get_tracepoint_struct( format_file, category, event_name, bpftrace); } } } } return true; } std::string TracepointFormatParser::get_struct_name( const std::string &category, const std::string &event_name) { return "struct _tracepoint_" + category + "_" + event_name; } std::string TracepointFormatParser::get_struct_name(const std::string &probe_id) { // probe_id has format category:event std::string event_name = probe_id; std::string category = erase_prefix(event_name); return get_struct_name(category, event_name); } std::string TracepointFormatParser::parse_field(const std::string &line, int *last_offset, BPFtrace &bpftrace) { std::string extra = ""; auto field_pos = line.find("field:"); if (field_pos == std::string::npos) return ""; auto field_semi_pos = line.find(';', field_pos); if (field_semi_pos == std::string::npos) return ""; auto offset_pos = line.find("offset:", field_semi_pos); if (offset_pos == std::string::npos) return ""; auto offset_semi_pos = line.find(';', offset_pos); if (offset_semi_pos == std::string::npos) return ""; auto size_pos = line.find("size:", offset_semi_pos); if (size_pos == std::string::npos) return ""; auto size_semi_pos = line.find(';', size_pos); if (size_semi_pos == std::string::npos) return ""; int size = std::stoi(line.substr(size_pos + 5, size_semi_pos - size_pos - 5)); int offset = std::stoi( line.substr(offset_pos + 7, offset_semi_pos - offset_pos - 7)); // If there'a gap between last field and this one, // generate padding fields if (offset && *last_offset) { int i, gap = offset - *last_offset; for (i = 0; i < gap; i++) { extra += " char __pad_" + std::to_string(offset - gap + i) + ";\n"; } } *last_offset = offset + size; std::string field = line.substr(field_pos + 6, field_semi_pos - field_pos - 6); auto field_type_end_pos = field.find_last_of("\t "); if (field_type_end_pos == std::string::npos) return ""; std::string field_type = field.substr(0, field_type_end_pos); std::string field_name = field.substr(field_type_end_pos + 1); if (field_type.find("__data_loc") != std::string::npos) { // Note that the type here (ie `int`) does not matter. Later during parse // time the parser will rewrite this field type to a u64 so that it can // hold the pointer to the actual location of the data. field_type = R"_(__attribute__((annotate("tp_data_loc"))) int)_"; } auto arr_size_pos = field_name.find('['); auto arr_size_end_pos = field_name.find(']'); // Only adjust field types for non-arrays if (arr_size_pos == std::string::npos) field_type = adjust_integer_types(field_type, size); // If BTF is available, we try not to use any header files, including // and request all the types we need from BTF. bpftrace.btf_set_.emplace(field_type); if (arr_size_pos != std::string::npos) { auto arr_size = field_name.substr(arr_size_pos + 1, arr_size_end_pos - arr_size_pos - 1); if (arr_size.find_first_not_of("0123456789") != std::string::npos) bpftrace.btf_set_.emplace(arr_size); } return extra + " " + field_type + " " + field_name + ";\n"; } std::string TracepointFormatParser::adjust_integer_types( const std::string &field_type, int size) { std::string new_type = field_type; // Adjust integer fields to correctly sized types if (size == 8) { if (field_type == "int") new_type = "s64"; if (field_type == "unsigned int" || field_type == "unsigned" || field_type == "u32" || field_type == "pid_t" || field_type == "uid_t" || field_type == "gid_t") new_type = "u64"; } return new_type; } std::string TracepointFormatParser::get_tracepoint_struct( std::istream &format_file, const std::string &category, const std::string &event_name, BPFtrace &bpftrace) { std::string format_struct = get_struct_name(category, event_name) + "\n{\n"; int last_offset = 0; for (std::string line; getline(format_file, line);) { format_struct += parse_field(line, &last_offset, bpftrace); } format_struct += "};\n"; return format_struct; } } // namespace bpftrace bpftrace-0.23.2/src/tracepoint_format_parser.h000066400000000000000000000036151477746507000214460ustar00rootroot00000000000000#pragma once #include #include #include "ast/visitor.h" #include "bpftrace.h" namespace bpftrace { namespace ast { class TracepointArgsVisitor : public Visitor { public: explicit TracepointArgsVisitor(ASTContext &ctx) : Visitor(ctx) { } using Visitor::visit; void visit(Builtin &builtin) { Visitor::visit(builtin); if (builtin.ident == "args" && probe_->tp_args_structs_level == -1) probe_->tp_args_structs_level = 0; }; void visit(FieldAccess &acc) { Visitor::visit(acc); if (probe_->tp_args_structs_level >= 0) probe_->tp_args_structs_level++; }; void visit(Probe &probe) { probe_ = &probe; Visitor::visit(probe); }; private: Probe *probe_; }; } // namespace ast class TracepointFormatParser { public: static bool parse(ast::ASTContext &ctx, BPFtrace &bpftrace); static std::string get_struct_name(const std::string &category, const std::string &event_name); static std::string get_struct_name(const std::string &probe_id); static void clear_struct_list() { struct_list.clear(); } private: static std::string parse_field(const std::string &line, int *last_offset, BPFtrace &bpftrace); static std::string adjust_integer_types(const std::string &field_type, int size); static std::set struct_list; protected: static std::string get_tracepoint_struct(std::istream &format_file, const std::string &category, const std::string &event_name, BPFtrace &bpftrace); }; } // namespace bpftrace bpftrace-0.23.2/src/types.cpp000066400000000000000000000426121477746507000160510ustar00rootroot00000000000000#include #include #include #include #include "ast/async_event_types.h" #include "bpftrace.h" #include "log.h" #include "struct.h" #include "types.h" #include "utils.h" namespace bpftrace { std::ostream &operator<<(std::ostream &os, Type type) { os << typestr(type); return os; } std::ostream &operator<<(std::ostream &os, AddrSpace as) { os << addrspacestr(as); return os; } std::ostream &operator<<(std::ostream &os, ProbeType type) { os << probetypeName(type); return os; } std::ostream &operator<<(std::ostream &os, const SizedType &type) { os << typestr(type); return os; } std::string typestr(const SizedType &type) { switch (type.GetTy()) { case Type::integer: if (type.IsEnumTy()) { return "enum " + type.GetName(); } return (type.is_signed_ ? "int" : "uint") + std::to_string(8 * type.GetSize()); case Type::inet: case Type::string: case Type::buffer: return typestr(type.GetTy()) + "[" + std::to_string(type.GetSize()) + "]"; case Type::pointer: { std::string prefix; if (type.IsCtxAccess()) prefix = "(ctx) "; return prefix + typestr(*type.GetPointeeTy()) + " *"; } case Type::array: return typestr(*type.GetElementTy()) + "[" + std::to_string(type.GetNumElements()) + "]"; case Type::record: return type.GetName(); case Type::reference: return typestr(*type.GetDereferencedTy()) + " &"; case Type::tuple: { std::string res = "("; size_t n = type.GetFieldCount(); for (size_t i = 0; i < n; ++i) { res += typestr(type.GetField(i).type); if (i != n - 1) res += ","; } res += ")"; return res; } case Type::max_t: case Type::min_t: case Type::sum_t: case Type::avg_t: case Type::count_t: case Type::stats_t: return (type.is_signed_ ? "" : "u") + typestr(type.GetTy()); case Type::mac_address: case Type::kstack_t: case Type::ustack_t: case Type::timestamp: case Type::ksym_t: case Type::usym_t: case Type::username: case Type::stack_mode: case Type::timestamp_mode: case Type::cgroup_path_t: case Type::strerror_t: case Type::hist_t: case Type::lhist_t: case Type::none: case Type::voidtype: return typestr(type.GetTy()); } __builtin_unreachable(); } std::string to_string(Type ty) { std::ostringstream ss; ss << ty; return ss.str(); } bool SizedType::IsSameType(const SizedType &t) const { if (t.GetTy() != type_) return false; if (IsRecordTy()) return t.GetName() == GetName(); if (IsPtrTy() && t.IsPtrTy()) return GetPointeeTy()->IsSameType(*t.GetPointeeTy()); if (IsTupleTy() && t.IsTupleTy()) { if (GetFieldCount() != t.GetFieldCount()) return false; for (ssize_t i = 0; i < GetFieldCount(); i++) { if (!GetField(i).type.IsSameType(t.GetField(i).type)) return false; } } return type_ == t.GetTy(); } bool SizedType::IsEqual(const SizedType &t) const { if (t.GetTy() != type_) return false; if (IsRecordTy()) return t.GetName() == GetName() && t.GetSize() == GetSize(); if (IsPtrTy()) return *t.GetPointeeTy() == *GetPointeeTy(); if (IsArrayTy()) return t.GetNumElements() == GetNumElements() && *t.GetElementTy() == *GetElementTy(); if (IsTupleTy()) return *t.GetStruct().lock() == *GetStruct().lock(); return type_ == t.GetTy() && GetSize() == t.GetSize() && is_signed_ == t.is_signed_; } bool SizedType::operator!=(const SizedType &t) const { return !IsEqual(t); } bool SizedType::operator==(const SizedType &t) const { return IsEqual(t); } bool SizedType::IsByteArray() const { return type_ == Type::string || type_ == Type::usym_t || type_ == Type::inet || type_ == Type::buffer || type_ == Type::timestamp || type_ == Type::mac_address || type_ == Type::cgroup_path_t; } bool SizedType::IsAggregate() const { return IsArrayTy() || IsByteArray() || IsTupleTy() || IsRecordTy() || IsStack(); } bool SizedType::IsStack() const { return type_ == Type::ustack_t || type_ == Type::kstack_t; } std::string addrspacestr(AddrSpace as) { switch (as) { case AddrSpace::kernel: return "kernel"; break; case AddrSpace::user: return "user"; break; case AddrSpace::bpf: return "bpf"; break; case AddrSpace::none: return "none"; break; } return {}; // unreached } std::string typestr(Type t) { switch (t) { // clang-format off case Type::none: return "none"; break; case Type::voidtype: return "void"; break; case Type::integer: return "int"; break; case Type::pointer: return "pointer"; break; case Type::reference:return "reference";break; case Type::record: return "record"; break; case Type::hist_t: return "hist_t"; break; case Type::lhist_t: return "lhist_t"; break; case Type::count_t: return "count_t"; break; case Type::sum_t: return "sum_t"; break; case Type::min_t: return "min_t"; break; case Type::max_t: return "max_t"; break; case Type::avg_t: return "avg_t"; break; case Type::stats_t: return "stats_t"; break; case Type::kstack_t: return "kstack"; break; case Type::ustack_t: return "ustack"; break; case Type::string: return "string"; break; case Type::ksym_t: return "ksym_t"; break; case Type::usym_t: return "usym_t"; break; case Type::username: return "username"; break; case Type::inet: return "inet"; break; case Type::stack_mode:return "stack_mode";break; case Type::array: return "array"; break; case Type::buffer: return "buffer"; break; case Type::tuple: return "tuple"; break; case Type::timestamp:return "timestamp";break; case Type::mac_address: return "mac_address"; break; case Type::cgroup_path_t: return "cgroup_path_t"; break; case Type::strerror_t: return "strerror_t"; break; case Type::timestamp_mode: return "timestamp_mode"; break; // clang-format on } return {}; // unreached } ProbeType probetype(const std::string &probeName) { ProbeType retType = ProbeType::invalid; auto v = std::find_if(PROBE_LIST.begin(), PROBE_LIST.end(), [&probeName](const ProbeItem &p) { return (p.name == probeName || p.aliases.find(probeName) != p.aliases.end()); }); if (v != PROBE_LIST.end()) retType = v->type; return retType; } std::string expand_probe_name(const std::string &orig_name) { std::string expanded_name = orig_name; auto v = std::find_if(PROBE_LIST.begin(), PROBE_LIST.end(), [&orig_name](const ProbeItem &p) { return (p.name == orig_name || p.aliases.find(orig_name) != p.aliases.end()); }); if (v != PROBE_LIST.end()) expanded_name = v->name; return expanded_name; } std::string probetypeName(ProbeType t) { // clang-format off switch (t) { case ProbeType::invalid: return "invalid"; break; case ProbeType::special: return "special"; break; case ProbeType::kprobe: return "kprobe"; break; case ProbeType::kretprobe: return "kretprobe"; break; case ProbeType::uprobe: return "uprobe"; break; case ProbeType::uretprobe: return "uretprobe"; break; case ProbeType::usdt: return "usdt"; break; case ProbeType::tracepoint: return "tracepoint"; break; case ProbeType::profile: return "profile"; break; case ProbeType::interval: return "interval"; break; case ProbeType::software: return "software"; break; case ProbeType::hardware: return "hardware"; break; case ProbeType::watchpoint: return "watchpoint"; break; case ProbeType::asyncwatchpoint: return "asyncwatchpoint"; break; case ProbeType::fentry: return "fentry"; break; case ProbeType::fexit: return "fexit"; break; case ProbeType::iter: return "iter"; break; case ProbeType::rawtracepoint: return "rawtracepoint"; break; } // clang-format on return {}; // unreached } uint64_t asyncactionint(AsyncAction a) { return static_cast(a); } // Type wrappers SizedType CreateInteger(size_t bits, bool is_signed) { auto t = SizedType(Type::integer, 0, is_signed); t.SetIntBitWidth(bits); return t; } SizedType CreateBool() { return CreateInteger(1, false); } SizedType CreateInt(size_t bits) { return CreateInteger(bits, true); }; SizedType CreateUInt(size_t bits) { return CreateInteger(bits, false); } SizedType CreateInt8() { return CreateInt(8); } SizedType CreateInt16() { return CreateInt(16); } SizedType CreateInt32() { return CreateInt(32); } SizedType CreateInt64() { return CreateInt(64); } SizedType CreateUInt8() { return CreateUInt(8); } SizedType CreateUInt16() { return CreateUInt(16); } SizedType CreateUInt32() { return CreateUInt(32); } SizedType CreateUInt64() { return CreateUInt(64); } SizedType CreateEnum(size_t bits, const std::string &name) { auto ty = CreateUInt(bits); ty.name_ = name; return ty; } SizedType CreateString(size_t size) { return SizedType(Type::string, size); } SizedType CreateNone() { return SizedType(Type::none, 0); } SizedType CreateVoid() { return SizedType(Type::voidtype, 0); } SizedType CreateStackMode() { return SizedType(Type::stack_mode, 0); } SizedType CreateArray(size_t num_elements, const SizedType &element_type) { size_t size = num_elements * element_type.GetSize(); auto ty = SizedType(Type::array, size); ty.element_type_ = std::make_shared(element_type); ty.num_elements_ = num_elements; return ty; } SizedType CreatePointer(const SizedType &pointee_type, AddrSpace as) { // Pointer itself is always an uint64 auto ty = SizedType(Type::pointer, 8); ty.element_type_ = std::make_shared(pointee_type); ty.SetAS(as); return ty; } SizedType CreateReference(const SizedType &referred_type, AddrSpace as) { // Reference itself is always an uint64 auto ty = SizedType(Type::reference, 8); ty.element_type_ = std::make_shared(referred_type); ty.SetAS(as); return ty; } SizedType CreateRecord(const std::string &name, std::weak_ptr record) { auto ty = SizedType(Type::record, record.expired() ? 0 : record.lock()->size); ty.name_ = name; ty.inner_struct_ = record; return ty; } SizedType CreateStack(bool kernel, StackType stack) { // These sizes are based on the stack key (see // IRBuilderBPF::GetStackStructType) but include struct padding auto st = SizedType(kernel ? Type::kstack_t : Type::ustack_t, kernel ? 16 : 24); st.stack_type = stack; return st; } SizedType CreateMin(bool is_signed) { return SizedType(Type::min_t, 8, is_signed); } SizedType CreateMax(bool is_signed) { return SizedType(Type::max_t, 8, is_signed); } SizedType CreateSum(bool is_signed) { return SizedType(Type::sum_t, 8, is_signed); } SizedType CreateCount(bool is_signed) { return SizedType(Type::count_t, 8, is_signed); } SizedType CreateAvg(bool is_signed) { return SizedType(Type::avg_t, 8, is_signed); } SizedType CreateStats(bool is_signed) { return SizedType(Type::stats_t, 8, is_signed); } SizedType CreateUsername() { return SizedType(Type::username, 8); } SizedType CreateInet(size_t size) { auto st = SizedType(Type::inet, size); st.is_internal = true; return st; } SizedType CreateLhist() { return SizedType(Type::lhist_t, 8); } SizedType CreateHist() { return SizedType(Type::hist_t, 8); } SizedType CreateUSym() { return SizedType(Type::usym_t, 16); } SizedType CreateKSym() { return SizedType(Type::ksym_t, 8); } SizedType CreateBuffer(size_t size) { auto metadata_headroom_bytes = sizeof(AsyncEvent::Buf); return SizedType(Type::buffer, size + metadata_headroom_bytes); } SizedType CreateTimestamp() { return SizedType(Type::timestamp, 16); } SizedType CreateTuple(std::weak_ptr tuple) { auto s = SizedType(Type::tuple, tuple.lock()->size); s.inner_struct_ = tuple; return s; } SizedType CreateMacAddress() { auto st = SizedType(Type::mac_address, 6); st.is_internal = true; return st; } SizedType CreateCgroupPath() { return SizedType(Type::cgroup_path_t, 16); } SizedType CreateStrerror() { return SizedType(Type::strerror_t, 8); } SizedType CreateTimestampMode() { return SizedType(Type::timestamp_mode, 0); } bool SizedType::IsSigned() const { return is_signed_; } std::vector &SizedType::GetFields() const { assert(IsTupleTy() || IsRecordTy()); return inner_struct_.lock()->fields; } Field &SizedType::GetField(ssize_t n) const { assert(IsTupleTy() || IsRecordTy()); if (n >= GetFieldCount()) throw FatalUserException("Getfield(): out of bounds"); return inner_struct_.lock()->fields[n]; } ssize_t SizedType::GetFieldCount() const { assert(IsTupleTy() || IsRecordTy()); return inner_struct_.lock()->fields.size(); } void SizedType::DumpStructure(std::ostream &os) { assert(IsTupleTy()); if (IsTupleTy()) os << "tuple"; else os << "struct"; return inner_struct_.lock()->Dump(os); } ssize_t SizedType::GetInTupleAlignment() const { if (IsByteArray()) return 1; if (IsTupleTy() || IsRecordTy()) return inner_struct_.lock()->align; if (GetSize() <= 2) return GetSize(); else if (IsArrayTy()) return element_type_->GetInTupleAlignment(); else if (GetSize() <= 4) return 4; else return 8; } bool SizedType::HasField(const std::string &name) const { assert(IsRecordTy()); return inner_struct_.lock()->HasField(name); } const Field &SizedType::GetField(const std::string &name) const { assert(IsRecordTy()); return inner_struct_.lock()->GetField(name); } std::weak_ptr SizedType::GetStruct() const { assert(IsRecordTy() || IsTupleTy()); return inner_struct_; } bool SizedType::IsSameSizeRecursive(const SizedType &t) const { if (GetSize() != t.GetSize()) { return false; } if (IsTupleTy() && t.IsTupleTy()) { if (GetFieldCount() != t.GetFieldCount()) { return false; } for (ssize_t i = 0; i < GetFieldCount(); i++) { if (!GetField(i).type.IsSameSizeRecursive(t.GetField(i).type)) return false; } } return true; } bool SizedType::FitsInto(const SizedType &t) const { if (!IsSameType(t)) return false; if (IsStringTy() && t.IsStringTy()) return GetSize() <= t.GetSize(); if (IsIntegerTy()) { if (IsSigned() == t.IsSigned()) return GetSize() <= t.GetSize(); // Unsigned into signed requires the destination to be bigger than the // source, e.g. uint32 -> int64. uint32 does not fit into int32. if (!IsSigned()) return GetSize() < t.GetSize(); return false; // signed never fits into unsigned } if (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); } bool SizedType::NeedsPercpuMap() const { return IsHistTy() || IsLhistTy() || IsCountTy() || IsSumTy() || IsMinTy() || IsMaxTy() || IsAvgTy() || IsStatsTy(); } } // namespace bpftrace namespace std { size_t hash::operator()( const bpftrace::SizedType &type) const { auto hash = std::hash()(static_cast(type.GetTy())); bpftrace::hash_combine(hash, type.GetSize()); switch (type.GetTy()) { case bpftrace::Type::integer: bpftrace::hash_combine(hash, type.IsSigned()); break; case bpftrace::Type::pointer: bpftrace::hash_combine(hash, *type.GetPointeeTy()); break; case bpftrace::Type::reference: bpftrace::hash_combine(hash, *type.GetDereferencedTy()); break; case bpftrace::Type::record: bpftrace::hash_combine(hash, type.GetName()); break; case bpftrace::Type::kstack_t: case bpftrace::Type::ustack_t: bpftrace::hash_combine(hash, type.stack_type); break; case bpftrace::Type::array: bpftrace::hash_combine(hash, *type.GetElementTy()); bpftrace::hash_combine(hash, type.GetNumElements()); break; case bpftrace::Type::tuple: bpftrace::hash_combine(hash, *type.GetStruct().lock()); break; // No default case (explicitly skip all remaining types instead) to get // a compiler warning when we add a new type case bpftrace::Type::none: case bpftrace::Type::voidtype: case bpftrace::Type::hist_t: case bpftrace::Type::lhist_t: case bpftrace::Type::count_t: case bpftrace::Type::sum_t: case bpftrace::Type::min_t: case bpftrace::Type::max_t: case bpftrace::Type::avg_t: case bpftrace::Type::stats_t: case bpftrace::Type::string: case bpftrace::Type::ksym_t: case bpftrace::Type::usym_t: case bpftrace::Type::username: case bpftrace::Type::inet: case bpftrace::Type::stack_mode: case bpftrace::Type::buffer: case bpftrace::Type::timestamp: case bpftrace::Type::mac_address: case bpftrace::Type::cgroup_path_t: case bpftrace::Type::strerror_t: case bpftrace::Type::timestamp_mode: break; } return hash; } } // namespace std bpftrace-0.23.2/src/types.h000066400000000000000000000444141477746507000155200ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace bpftrace { const int MAX_STACK_SIZE = 1024; const int DEFAULT_STACK_SIZE = 127; const int COMM_SIZE = 16; enum class Type : uint8_t { // clang-format off none, voidtype, integer, // int is a protected keyword pointer, reference, record, // struct/union, as struct is a protected keyword hist_t, lhist_t, count_t, sum_t, min_t, max_t, avg_t, stats_t, kstack_t, ustack_t, string, ksym_t, usym_t, username, inet, stack_mode, array, buffer, tuple, timestamp, mac_address, cgroup_path_t, strerror_t, timestamp_mode, // clang-format on }; enum class AddrSpace : uint8_t { none, kernel, user, bpf, }; std::ostream &operator<<(std::ostream &os, Type type); std::ostream &operator<<(std::ostream &os, AddrSpace as); std::string to_string(Type ty); enum class UserSymbolCacheType { per_pid, per_program, none, }; enum class StackMode : uint8_t { bpftrace, perf, raw, }; const std::map STACK_MODE_NAME_MAP = { { StackMode::bpftrace, "bpftrace" }, { StackMode::perf, "perf" }, { StackMode::raw, "raw" }, }; struct StackType { uint16_t limit = DEFAULT_STACK_SIZE; StackMode mode = StackMode::bpftrace; bool operator==(const StackType &obj) const { return limit == obj.limit && mode == obj.mode; } std::string name() const { return "stack_" + STACK_MODE_NAME_MAP.at(mode) + "_" + std::to_string(limit); } static const std::string &scratch_name() { static const std::string scratch_name = "stack_scratch"; return scratch_name; } private: friend class cereal::access; template void serialize(Archive &archive) { archive(limit, mode); } }; enum class TimestampMode : uint8_t { monotonic, boot, tai, sw_tai, }; struct Struct; struct Field; class SizedType { public: SizedType() : type_(Type::none) { } SizedType(Type type, size_t size_, bool is_signed) : type_(type), size_bits_(size_ * 8), is_signed_(is_signed) { } SizedType(Type type, size_t size_) : type_(type), size_bits_(size_ * 8) { } StackType stack_type; int funcarg_idx = -1; bool is_internal = false; bool is_tparg = false; bool is_funcarg = false; bool is_btftype = false; TimestampMode ts_mode = TimestampMode::boot; private: Type type_; size_t size_bits_ = 0; // size in bits std::shared_ptr element_type_; // for "container" and pointer // (like) types std::string name_; // name of this type, for named types like struct and enum std::weak_ptr inner_struct_; // inner struct for records and tuples // the actual Struct object is owned by // StructManager AddrSpace as_ = AddrSpace::none; bool is_signed_ = false; bool ctx_ = false; // Is bpf program context std::unordered_set btf_type_tags_ = {}; // Only populated for // Type::pointer size_t num_elements_ = 0; // Only populated for array types friend class cereal::access; template void serialize(Archive &archive) { archive(type_, stack_type, is_internal, is_tparg, is_funcarg, is_btftype, funcarg_idx, is_signed_, element_type_, name_, ctx_, as_, size_bits_, inner_struct_); } public: // Tuple/struct accessors std::vector &GetFields() const; bool HasField(const std::string &name) const; const Field &GetField(const std::string &name) const; Field &GetField(ssize_t n) const; ssize_t GetFieldCount() const; std::weak_ptr GetStruct() const; // Required alignment for this type when used inside a tuple ssize_t GetInTupleAlignment() const; // Dump the underlying structure for debug purposes void DumpStructure(std::ostream &os); AddrSpace GetAS() const { return as_; } void SetAS(AddrSpace as) { as_ = as; } void SetBtfTypeTags(std::unordered_set &&tags) { assert(IsPtrTy()); btf_type_tags_ = std::move(tags); } const std::unordered_set &GetBtfTypeTags() const { assert(IsPtrTy()); return btf_type_tags_; } bool IsCtxAccess() const { return ctx_; }; void MarkCtxAccess() { ctx_ = true; }; bool IsByteArray() const; bool IsAggregate() const; bool IsStack() const; bool IsEqual(const SizedType &t) const; bool operator==(const SizedType &t) const; bool operator!=(const SizedType &t) const; bool IsSameType(const SizedType &t) const; // This is primarily for Tuples which have equal total size (due to padding) // but their individual elements have different sizes. bool IsSameSizeRecursive(const SizedType &t) const; bool FitsInto(const SizedType &t) const; bool IsPrintableTy() { return type_ != Type::none && type_ != Type::stack_mode && type_ != Type::timestamp_mode && (!IsCtxAccess() || is_funcarg); // args builtin is printable } void SetSign(bool is_signed) { is_signed_ = is_signed; } bool IsSigned(void) const; size_t GetSize() const { return size_bits_ / 8; } void SetSize(size_t byte_size) { if (IsIntTy()) SetIntBitWidth(byte_size * 8); else size_bits_ = byte_size * 8; } void SetIntBitWidth(size_t bits) { assert(IsIntTy()); // Truncate integers too large to fit in BPF registers (64-bits). if (bits > 64) bits = 64; // Zero sized integers are not usually valid. However, during semantic // analysis when we're inferring types, the first pass may not have // enough information to figure out the exact size of the integer. Later // passes infer the exact size. assert(bits == 0 || bits == 1 || bits == 8 || bits == 16 || bits == 32 || bits == 64); size_bits_ = bits; } size_t GetIntBitWidth() const { assert(IsIntTy()); return size_bits_; }; size_t GetNumElements() const { assert(IsArrayTy()); // For arrays we can't just do size_bits_ / element_type.GetSize() // because we might not know the size of the element type (it may be 0) // if it's being resolved in a later AST pass e.g. for an imported type: // `let $x: struct Foo[10];` return num_elements_; }; const std::string GetName() const { assert(IsRecordTy() || IsEnumTy()); return name_; } Type GetTy() const { return type_; } const SizedType *GetElementTy() const { assert(IsArrayTy()); return element_type_.get(); } const SizedType *GetPointeeTy() const { assert(IsPtrTy()); return element_type_.get(); } const SizedType *GetDereferencedTy() const { assert(IsRefTy()); return element_type_.get(); } bool IsBoolTy() const { return type_ == Type::integer && size_bits_ == 1; }; bool IsPtrTy() const { return type_ == Type::pointer; }; bool IsRefTy() const { return type_ == Type::reference; }; bool IsIntTy() const { return type_ == Type::integer; }; bool IsEnumTy() const { return IsIntTy() && name_.size(); } bool IsNoneTy(void) const { return type_ == Type::none; }; bool IsVoidTy(void) const { return type_ == Type::voidtype; }; bool IsIntegerTy(void) const { return type_ == Type::integer; }; bool IsHistTy(void) const { return type_ == Type::hist_t; }; bool IsLhistTy(void) const { return type_ == Type::lhist_t; }; bool IsCountTy(void) const { return type_ == Type::count_t; }; bool IsSumTy(void) const { return type_ == Type::sum_t; }; bool IsMinTy(void) const { return type_ == Type::min_t; }; bool IsMaxTy(void) const { return type_ == Type::max_t; }; bool IsAvgTy(void) const { return type_ == Type::avg_t; }; bool IsStatsTy(void) const { return type_ == Type::stats_t; }; bool IsKstackTy(void) const { return type_ == Type::kstack_t; }; bool IsUstackTy(void) const { return type_ == Type::ustack_t; }; bool IsStringTy(void) const { return type_ == Type::string; }; bool IsKsymTy(void) const { return type_ == Type::ksym_t; }; bool IsUsymTy(void) const { return type_ == Type::usym_t; }; bool IsUsernameTy(void) const { return type_ == Type::username; }; bool IsInetTy(void) const { return type_ == Type::inet; }; bool IsStackModeTy(void) const { return type_ == Type::stack_mode; }; bool IsArrayTy(void) const { return type_ == Type::array; }; bool IsRecordTy(void) const { return type_ == Type::record; }; bool IsBufferTy(void) const { return type_ == Type::buffer; }; bool IsTupleTy(void) const { return type_ == Type::tuple; }; bool IsTimestampTy(void) const { return type_ == Type::timestamp; }; bool IsMacAddressTy(void) const { return type_ == Type::mac_address; }; bool IsCgroupPathTy(void) const { return type_ == Type::cgroup_path_t; }; bool IsStrerrorTy(void) const { return type_ == Type::strerror_t; }; bool IsTimestampModeTy(void) const { return type_ == Type::timestamp_mode; } bool IsCastableMapTy() const { return type_ == Type::count_t || type_ == Type::sum_t || type_ == Type::max_t || type_ == Type::min_t || type_ == Type::avg_t; } bool IsMapIterableTy() const { return !IsMultiOutputMapTy(); } // These are special map value types that can't be reduced to a single value // and output multiple lines when printed bool IsMultiOutputMapTy() const { return type_ == Type::hist_t || type_ == Type::lhist_t || type_ == Type::stats_t; } bool NeedsPercpuMap() const; friend std::string typestr(const SizedType &type); void IntoPointer() { assert(IsRefTy()); type_ = Type::pointer; } // Factories friend SizedType CreateEnum(size_t bits, const std::string &name); friend SizedType CreateArray(size_t num_elements, const SizedType &element_type); friend SizedType CreatePointer(const SizedType &pointee_type, AddrSpace as); friend SizedType CreateReference(const SizedType &pointee_type, AddrSpace as); friend SizedType CreateRecord(const std::string &name, std::weak_ptr record); friend SizedType CreateInteger(size_t bits, bool is_signed); friend SizedType CreateTuple(std::weak_ptr tuple); }; // Type helpers SizedType CreateNone(); SizedType CreateVoid(); SizedType CreateBool(); SizedType CreateInteger(size_t bits, bool is_signed); SizedType CreateInt(size_t bits); SizedType CreateUInt(size_t bits); SizedType CreateInt8(); SizedType CreateInt16(); SizedType CreateInt32(); SizedType CreateInt64(); SizedType CreateUInt8(); SizedType CreateUInt16(); SizedType CreateUInt32(); SizedType CreateUInt64(); SizedType CreateEnum(size_t bits, const std::string &name); SizedType CreateString(size_t size); SizedType CreateArray(size_t num_elements, const SizedType &element_type); SizedType CreatePointer(const SizedType &pointee_type, AddrSpace as = AddrSpace::none); SizedType CreateReference(const SizedType &referred_type, AddrSpace as = AddrSpace::none); SizedType CreateRecord(const std::string &name, std::weak_ptr record); SizedType CreateTuple(std::weak_ptr tuple); SizedType CreateStackMode(); SizedType CreateStack(bool kernel, StackType st = StackType()); SizedType CreateMin(bool is_signed); SizedType CreateMax(bool is_signed); SizedType CreateSum(bool is_signed); SizedType CreateCount(bool is_signed); SizedType CreateAvg(bool is_signed); SizedType CreateStats(bool is_signed); SizedType CreateUsername(); SizedType CreateInet(size_t size); SizedType CreateLhist(); SizedType CreateHist(); SizedType CreateUSym(); SizedType CreateKSym(); SizedType CreateBuffer(size_t size); SizedType CreateTimestamp(); SizedType CreateMacAddress(); SizedType CreateCgroupPath(); SizedType CreateStrerror(); SizedType CreateTimestampMode(); std::ostream &operator<<(std::ostream &os, const SizedType &type); enum class ProbeType { invalid, special, kprobe, kretprobe, uprobe, uretprobe, usdt, tracepoint, profile, interval, software, hardware, watchpoint, asyncwatchpoint, fentry, fexit, iter, rawtracepoint, }; std::ostream &operator<<(std::ostream &os, ProbeType type); struct ProbeItem { std::string name; std::unordered_set aliases; ProbeType type; // these are used in bpftrace -l // to show which probes are available to attach to bool show_in_kernel_list = false; bool show_in_userspace_list = false; }; const std::vector PROBE_LIST = { { .name = "kprobe", .aliases = { "k" }, .type = ProbeType::kprobe, .show_in_kernel_list = true }, { .name = "kretprobe", .aliases = { "kr" }, .type = ProbeType::kretprobe }, { .name = "uprobe", .aliases = { "u" }, .type = ProbeType::uprobe, .show_in_userspace_list = true }, { .name = "uretprobe", .aliases = { "ur" }, .type = ProbeType::uretprobe }, { .name = "usdt", .aliases = { "U" }, .type = ProbeType::usdt, .show_in_userspace_list = true }, { .name = "BEGIN", .aliases = { "BEGIN" }, .type = ProbeType::special }, { .name = "END", .aliases = { "END" }, .type = ProbeType::special }, { .name = "self", .aliases = { "self" }, .type = ProbeType::special }, { .name = "tracepoint", .aliases = { "t" }, .type = ProbeType::tracepoint, .show_in_kernel_list = true }, { .name = "profile", .aliases = { "p" }, .type = ProbeType::profile }, { .name = "interval", .aliases = { "i" }, .type = ProbeType::interval }, { .name = "software", .aliases = { "s" }, .type = ProbeType::software, .show_in_kernel_list = true }, { .name = "hardware", .aliases = { "h" }, .type = ProbeType::hardware, .show_in_kernel_list = true }, { .name = "watchpoint", .aliases = { "w" }, .type = ProbeType::watchpoint }, { .name = "asyncwatchpoint", .aliases = { "aw" }, .type = ProbeType::asyncwatchpoint }, { .name = "fentry", .aliases = { "f", "kfunc" }, .type = ProbeType::fentry, .show_in_kernel_list = true }, { .name = "fexit", .aliases = { "fr", "kretfunc" }, .type = ProbeType::fexit }, { .name = "iter", .aliases = { "it" }, .type = ProbeType::iter, .show_in_kernel_list = true }, { .name = "rawtracepoint", .aliases = { "rt" }, .type = ProbeType::rawtracepoint, .show_in_kernel_list = true }, }; ProbeType probetype(const std::string &type); std::string addrspacestr(AddrSpace as); std::string typestr(Type t); std::string typestr(const SizedType &type); std::string expand_probe_name(const std::string &orig_name); std::string probetypeName(ProbeType t); struct Probe { ProbeType type; std::string path; // file path if used std::string attach_point; // probe name (last component) 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 int usdt_location_idx = 0; // to disambiguate duplicate USDT markers uint64_t log_size = 1000000; int index = 0; int freq = 0; uint64_t len = 0; // for watchpoint probes, size of region std::string mode; // for watchpoint probes, watch mode (rwx) bool async = false; // for watchpoint probes, if it's an async watchpoint uint64_t address = 0; uint64_t func_offset = 0; std::vector funcs; private: friend class cereal::access; template void serialize(Archive &archive) { archive(type, path, attach_point, orig_name, name, pin, ns, loc, usdt_location_idx, log_size, index, freq, len, mode, async, address, func_offset, funcs); } }; const int RESERVED_IDS_PER_ASYNCACTION = 10000; enum class AsyncAction { // clang-format off printf = 0, // printf reserves 0-9999 for printf_ids syscall = 10000, // system reserves 10000-19999 for printf_ids cat = 20000, // cat reserves 20000-29999 for printf_ids exit = 30000, print, clear, zero, time, join, helper_error, print_non_map, strftime, watchpoint_attach, watchpoint_detach, skboutput, // clang-format on }; uint64_t asyncactionint(AsyncAction a); enum class PositionalParameterType { positional, count }; namespace globalvars { enum class GlobalVar { // Number of online CPUs at runtime, used for metric aggregation for functions // like sum and avg NUM_CPUS, // Max CPU ID returned by bpf_get_smp_processor_id, used for simulating // per-CPU maps in read-write global variables MAX_CPU_ID, // Scratch buffers used to avoid BPF stack allocation limits FMT_STRINGS_BUFFER, TUPLE_BUFFER, GET_STR_BUFFER, READ_MAP_VALUE_BUFFER, WRITE_MAP_VALUE_BUFFER, VARIABLE_BUFFER, MAP_KEY_BUFFER, }; } // namespace globalvars } // namespace bpftrace // SizedType hash function // Allows to use SizedType in unordered_set/map. namespace std { template <> struct hash { size_t operator()(const bpftrace::StackType &obj) const { switch (obj.mode) { case bpftrace::StackMode::bpftrace: return std::hash()("bpftrace#" + to_string(obj.limit)); case bpftrace::StackMode::perf: return std::hash()("perf#" + to_string(obj.limit)); case bpftrace::StackMode::raw: return std::hash()("raw#" + to_string(obj.limit)); } return {}; // unreached } }; template <> struct hash { size_t operator()(const bpftrace::SizedType &type) const; }; } // namespace std bpftrace-0.23.2/src/usdt.cpp000066400000000000000000000107621477746507000156650ustar00rootroot00000000000000#include "usdt.h" #include "log.h" #include "utils.h" #include #include #include #include #include #include #include static std::unordered_set path_cache; static std::unordered_set pid_cache; // Maps all traced paths and all their providers to vector of tracepoints // on each provider static std::unordered_map> usdt_provider_cache; // Maps a pid to a set of paths for its probes static std::unordered_map> usdt_pid_to_paths_cache; // Used as a temporary buffer, during read_probes_for_pid to maintain // current tracepoint paths for the current pid static std::unordered_set current_pid_paths; static void usdt_probe_each(struct bcc_usdt *usdt_probe) { usdt_provider_cache[usdt_probe->bin_path][usdt_probe->provider].emplace_back( usdt_probe_entry{ .path = usdt_probe->bin_path, .provider = usdt_probe->provider, .name = usdt_probe->name, .semaphore_offset = usdt_probe->semaphore_offset, .num_locations = usdt_probe->num_locations, }); current_pid_paths.emplace(usdt_probe->bin_path); } // Move the current pid paths onto the pid_to_paths_cache, and clear // current_pid_paths. static void cache_current_pid_paths(int pid) { usdt_pid_to_paths_cache[pid].merge(current_pid_paths); current_pid_paths.clear(); } std::optional USDTHelper::find(int pid, const std::string &target, const std::string &provider, const std::string &name) { usdt_probe_list probes; if (pid > 0) { read_probes_for_pid(pid); for (auto const &path : usdt_pid_to_paths_cache[pid]) { probes.insert(probes.end(), usdt_provider_cache[path][provider].begin(), usdt_provider_cache[path][provider].end()); } } else { read_probes_for_path(target); probes = usdt_provider_cache[target][provider]; } auto it = std::find_if(probes.begin(), probes.end(), [&name](const usdt_probe_entry &e) { return e.name == name; }); if (it != probes.end()) { return *it; } else { return std::nullopt; } } usdt_probe_list USDTHelper::probes_for_pid(int pid, bool print_error) { read_probes_for_pid(pid, print_error); usdt_probe_list probes; for (auto const &path : usdt_pid_to_paths_cache[pid]) { for (auto const &usdt_probes : usdt_provider_cache[path]) { probes.insert(probes.end(), usdt_probes.second.begin(), usdt_probes.second.end()); } } return probes; } usdt_probe_list USDTHelper::probes_for_all_pids() { usdt_probe_list probes; for (int pid : bpftrace::get_all_running_pids()) { for (auto &probe : probes_for_pid(pid, false)) { probes.push_back(std::move(probe)); } } return probes; } usdt_probe_list USDTHelper::probes_for_path(const std::string &path) { read_probes_for_path(path); usdt_probe_list probes; for (auto const &usdt_probes : usdt_provider_cache[path]) { probes.insert(probes.end(), usdt_probes.second.begin(), usdt_probes.second.end()); } return probes; } void USDTHelper::read_probes_for_pid(int pid, bool print_error) { if (pid_cache.count(pid)) return; if (pid > 0) { void *ctx = bcc_usdt_new_frompid(pid, nullptr); if (ctx == nullptr) { if (print_error) { LOG(ERROR) << "failed to initialize usdt context for pid: " << pid; if (kill(pid, 0) == -1 && errno == ESRCH) LOG(ERROR) << "hint: process not running"; } return; } bcc_usdt_foreach(ctx, usdt_probe_each); bcc_usdt_close(ctx); cache_current_pid_paths(pid); pid_cache.emplace(pid); } else { LOG(ERROR) << "a pid must be specified to list USDT probes by PID"; } } void USDTHelper::read_probes_for_path(const std::string &path) { if (path_cache.count(path)) return; void *ctx = bcc_usdt_new_frompath(path.c_str()); if (ctx == nullptr) { LOG(ERROR) << "failed to initialize usdt context for path " << path; return; } bcc_usdt_foreach(ctx, usdt_probe_each); bcc_usdt_close(ctx); path_cache.emplace(path); } bpftrace-0.23.2/src/usdt.h000066400000000000000000000021401477746507000153210ustar00rootroot00000000000000#pragma once #include #include #include #include struct usdt_probe_entry { std::string path; std::string provider; std::string name; uint64_t semaphore_offset; int num_locations; }; typedef std::vector usdt_probe_list; // Note this class is fully static because bcc_usdt_foreach takes a function // pointer callback without a context variable. So we must keep global state. class USDTHelper { public: virtual ~USDTHelper() = default; virtual std::optional find(int pid, const std::string &target, const std::string &provider, const std::string &name); static usdt_probe_list probes_for_pid(int pid, bool print_error = true); static usdt_probe_list probes_for_all_pids(); static usdt_probe_list probes_for_path(const std::string &path); private: static void read_probes_for_pid(int pid, bool print_error = true); static void read_probes_for_path(const std::string &path); }; bpftrace-0.23.2/src/usyms.cpp000066400000000000000000000116321477746507000160630ustar00rootroot00000000000000#include #include "config.h" #include "usyms.h" #include "scopeguard.h" namespace bpftrace { Usyms::Usyms(const Config &config) : config_(config) { } Usyms::~Usyms() { for (const auto &pair : exe_sym_) { if (pair.second.second) bcc_free_symcache(pair.second.second, pair.second.first); } for (const auto &pair : pid_sym_) { if (pair.second) bcc_free_symcache(pair.second, pair.first); } } void Usyms::cache(const std::string &elf_file) { auto cache_type = config_.get(ConfigKeyUserSymbolCacheType::default_); // preload symbol table for executable to make it available even if the // binary is not present at symbol resolution time // note: this only makes sense with ASLR disabled, since with ASLR offsets // might be different if (cache_type == UserSymbolCacheType::per_program && symbol_table_cache_.find(elf_file) == symbol_table_cache_.end()) symbol_table_cache_[elf_file] = get_symbol_table_for_elf(elf_file); if (cache_type == UserSymbolCacheType::per_pid) // preload symbol tables from running processes // this allows symbol resolution for processes that are running at probe // attach time, but not at symbol resolution time, even with ASLR // enabled, since BCC symcache records the offsets for (int pid : get_pids_for_program(elf_file)) pid_sym_[pid] = bcc_symcache_new(pid, &get_symbol_opts()); } std::string Usyms::resolve(uint64_t addr, int32_t pid, const std::string &pid_exe, bool show_offset, bool show_module) { auto cache_type = config_.get(ConfigKeyUserSymbolCacheType::default_); struct bcc_symbol usym; std::ostringstream symbol; void *psyms = nullptr; if (cache_type == UserSymbolCacheType::per_program) { if (!pid_exe.empty()) { // try to resolve symbol directly from program file // this might work when the process does not exist anymore, but cannot // resolve all symbols, e.g. those in a dynamically linked library std::map> &symbol_table = symbol_table_cache_.find(pid_exe) != symbol_table_cache_.end() ? symbol_table_cache_[pid_exe] : (symbol_table_cache_[pid_exe] = get_symbol_table_for_elf( pid_exe)); auto sym = symbol_table.lower_bound(addr); // address has to be either the start of the symbol (for symbols of // length 0) or in [start, end) if (sym != symbol_table.end() && (addr == sym->second.start || (addr >= sym->second.start && addr < sym->second.end))) { symbol << sym->second.name; if (show_offset) symbol << "+" << addr - sym->second.start; if (show_module) symbol << " (" << pid_exe << ")"; return symbol.str(); } } if (exe_sym_.find(pid_exe) == exe_sym_.end()) { // not cached, create new ProcSyms cache psyms = bcc_symcache_new(pid, &get_symbol_opts()); exe_sym_[pid_exe] = std::make_pair(pid, psyms); } else { psyms = exe_sym_[pid_exe].second; } } else if (cache_type == UserSymbolCacheType::per_pid) { // cache user symbols per pid if (pid_sym_.find(pid) == pid_sym_.end()) { // not cached, create new ProcSyms cache psyms = bcc_symcache_new(pid, &get_symbol_opts()); pid_sym_[pid] = psyms; } else { psyms = pid_sym_[pid]; } } else { // no user symbol caching, create new bcc cache psyms = bcc_symcache_new(pid, &get_symbol_opts()); } if (psyms && bcc_symcache_resolve(psyms, addr, &usym) == 0) { SCOPE_EXIT { // This is a horrible hack to work around the fact that // bcc does not tell if you if demangling succeeded. // B/c if demangling failed, it returns a string that // you cannot free. // // This relies on the fact that bcc will not change the // `demangle_name = name` fallback. Since blazesym is // coming (written 2/4/25), this should be fine for now. if (usym.demangle_name != usym.name) ::free(const_cast(usym.demangle_name)); }; if (config_.get(ConfigKeyBool::cpp_demangle)) symbol << usym.demangle_name; else symbol << usym.name; if (show_offset) symbol << "+" << usym.offset; if (show_module) symbol << " (" << usym.module << ")"; } else { symbol << reinterpret_cast(addr); if (show_module) symbol << " ([unknown])"; } if (cache_type == UserSymbolCacheType::none) bcc_free_symcache(psyms, pid); return symbol.str(); } struct bcc_symbol_option &Usyms::get_symbol_opts() { static struct bcc_symbol_option symopts = { .use_debug_file = 1, .check_debug_file_crc = 1, .lazy_symbolize = config_.get(ConfigKeyBool::lazy_symbolication) ? 1 : 0, .use_symbol_type = BCC_SYM_ALL_TYPES, }; return symopts; } } // namespace bpftrace bpftrace-0.23.2/src/usyms.h000066400000000000000000000017371477746507000155350ustar00rootroot00000000000000#pragma once #include #include #include #include "types.h" #include "utils.h" #include namespace bpftrace { class Config; class Usyms { public: Usyms(const Config& config); ~Usyms(); Usyms(Usyms&) = delete; Usyms& operator=(const Usyms&) = delete; void cache(const std::string& elf_file); std::string resolve(uint64_t addr, int32_t pid, const std::string& pid_exe, bool show_offset, bool show_module); private: const Config& config_; // note: exe_sym_ is used when layout is same for all instances of program std::map> exe_sym_; // exe -> (pid, cache) std::map pid_sym_; // pid -> cache std::map>> symbol_table_cache_; struct bcc_symbol_option& get_symbol_opts(); }; } // namespace bpftrace bpftrace-0.23.2/src/utils.cpp000066400000000000000000001365121477746507000160500ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bpftrace.h" #include "debugfs.h" #include "log.h" #include "probe_matcher.h" #include "scopeguard.h" #include "tracefs.h" #include "utils.h" namespace { std::vector read_cpu_range(std::string path) { std::ifstream cpus_range_stream{ path }; std::vector cpus; std::string cpu_range; while (std::getline(cpus_range_stream, cpu_range, ',')) { std::size_t rangeop = cpu_range.find('-'); if (rangeop == std::string::npos) { cpus.push_back(std::stoi(cpu_range)); } else { int start = std::stoi(cpu_range.substr(0, rangeop)); int end = std::stoi(cpu_range.substr(rangeop + 1)); for (int i = start; i <= end; i++) cpus.push_back(i); } } return cpus; } std::vector expand_wildcard_path(const std::string &path) { glob_t glob_result; memset(&glob_result, 0, sizeof(glob_result)); if (glob(path.c_str(), GLOB_NOCHECK, nullptr, &glob_result)) { globfree(&glob_result); throw bpftrace::FatalUserException("glob() failed"); } std::vector matching_paths; for (size_t i = 0; i < glob_result.gl_pathc; ++i) { matching_paths.push_back(std::string(glob_result.gl_pathv[i])); } globfree(&glob_result); return matching_paths; } std::vector expand_wildcard_paths( const std::vector &paths) { std::vector expanded_paths; for (const auto &p : paths) { auto ep = expand_wildcard_path(p); expanded_paths.insert(expanded_paths.end(), ep.begin(), ep.end()); } return expanded_paths; } } // namespace namespace bpftrace { //'borrowed' from libbpf's bpf_core_find_kernel_btf // from Andrii Nakryiko const struct vmlinux_location vmlinux_locs[] = { { "/sys/kernel/btf/vmlinux", true }, { "/boot/vmlinux-%1$s", false }, { "/lib/modules/%1$s/vmlinux-%1$s", false }, { "/lib/modules/%1$s/build/vmlinux", false }, { "/usr/lib/modules/%1$s/kernel/vmlinux", false }, { "/usr/lib/debug/boot/vmlinux-%1$s", false }, { "/usr/lib/debug/boot/vmlinux-%1$s.debug", false }, { "/usr/lib/debug/lib/modules/%1$s/vmlinux", false }, { nullptr, false }, }; std::optional find_vmlinux(struct vmlinux_location const *locs, struct symbol *sym) { struct utsname uts; if (uname(&uts) != 0) return std::nullopt; for (size_t i = 0; locs[i].path; ++i) { auto &loc = locs[i]; if (loc.raw) continue; // This file is for BTF. skip // Format the loc.path with the current release. char path[PATH_MAX] = {}; int bytes_written = std::snprintf( path, sizeof(path), loc.path, uts.release); if (bytes_written < 0) { LOG(ERROR) << "Failed to format vmlinux path '" << loc.path << "' using " << uts.release; continue; } else if (static_cast(bytes_written) > sizeof(path)) { LOG(WARNING) << "Truncated format for vmlinux path '" << loc.path << "' using " << uts.release; continue; } if (access(path, R_OK)) continue; if (sym == nullptr) { return path; } else { bcc_elf_symcb callback = !sym->name.empty() ? sym_name_cb : sym_address_cb; struct bcc_symbol_option options = { .use_debug_file = 0, .check_debug_file_crc = 0, .lazy_symbolize = 0, .use_symbol_type = BCC_SYM_ALL_TYPES ^ (1 << STT_NOTYPE), }; if (bcc_elf_foreach_sym(path, callback, &options, sym) == -1) { LOG(ERROR) << "Failed to iterate over symbols in " << path; continue; } if (sym->start) { LOG(V1) << "vmlinux: using " << path; return path; } } } return std::nullopt; } // find vmlinux file. // if sym is not null, check vmlinux contains the given symbol information std::optional find_vmlinux(struct symbol *sym) { struct vmlinux_location locs_env[] = { { std::getenv("BPFTRACE_VMLINUX"), false }, { nullptr, false }, }; return find_vmlinux(locs_env[0].path ? locs_env : vmlinux_locs, sym); } static bool pid_in_different_mountns(int pid); static std::vector resolve_binary_path(const std::string &cmd, const char *env_paths, int pid); void StdioSilencer::silence() { 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) throw bpftrace::FatalUserException(std::string(e.what()) + ": please raise NOFILE"); else LOG(BUG) << e.what(); } } StdioSilencer::~StdioSilencer() { if (old_stdio_ == -1) return; 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 (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(); } } KConfig::KConfig() { std::vector config_locs; // Try to get the config from BPFTRACE_KCONFIG_TEST env // If not set, use the set of default locations const char *path_env = std::getenv("BPFTRACE_KCONFIG_TEST"); if (path_env) config_locs = { std::string(path_env) }; else { struct utsname utsname; if (uname(&utsname) < 0) return; config_locs = { "/proc/config.gz", "/boot/config-" + std::string(utsname.release), }; } for (auto &path : config_locs) { // gzopen/gzgets handle both uncompressed and compressed files gzFile file = gzopen(path.c_str(), "r"); if (!file) continue; char buf[4096]; while (gzgets(file, buf, sizeof(buf))) { std::string option(buf); if (option.find("CONFIG_") == 0) { // trim trailing '\n' if (option[option.length() - 1] == '\n') option = option.substr(0, option.length() - 1); auto split = option.find("="); if (split == std::string::npos) continue; config.emplace(option.substr(0, split), option.substr(split + 1)); } } gzclose(file); break; } } void get_uint64_env_var(const ::std::string &str, const std::function &cb) { uint64_t dest; if (const char *env_p = std::getenv(str.c_str())) { std::istringstream stringstream(env_p); if (!(stringstream >> dest)) { throw bpftrace::FatalUserException( "Env var '" + str + "' did not contain a valid uint64_t, or was zero-valued."); return; } cb(dest); } } void get_bool_env_var(const ::std::string &str, const std::function &cb) { if (const char *env_p = std::getenv(str.c_str())) { bool dest; std::string s(env_p); if (s == "1") dest = true; else if (s == "0") dest = false; else { throw bpftrace::FatalUserException( "Env var '" + str + "' did not contain a valid value (0 or 1)."); } cb(dest); } return; } std::optional find_in_path(std::string_view name) { std::error_code ec; const char *path_env = std::getenv("PATH"); if (!path_env) return std::nullopt; auto paths = split_string(path_env, ':', true); for (const auto &path : paths) { auto fpath = std::filesystem::path(path) / name; if (std::filesystem::exists(fpath, ec)) return fpath; } return std::nullopt; } std::optional find_near_self(std::string_view filename) { std::error_code ec; auto exe = std::filesystem::read_symlink("/proc/self/exe", ec); if (ec) { LOG(WARNING) << "Failed to resolve /proc/self/exe: " << ec; return std::nullopt; } exe.replace_filename(filename); bool exists = std::filesystem::exists(exe, ec); if (!exists) { if (ec) LOG(WARNING) << "Failed to resolve stat " << exe << ": " << ec; return std::nullopt; } return exe; } std::string get_pid_exe(const std::string &pid) { std::error_code ec; std::filesystem::path proc_path{ "/proc" }; proc_path /= pid; proc_path /= "exe"; try { return std::filesystem::read_symlink(proc_path).string(); } catch (const std::filesystem::filesystem_error &e) { auto err = e.code().value(); if (err == ENOENT || err == EINVAL) return {}; else throw e; } } std::string get_pid_exe(pid_t pid) { return get_pid_exe(std::to_string(pid)); } std::string get_proc_maps(const std::string &pid) { std::error_code ec; std::filesystem::path proc_path{ "/proc" }; proc_path /= pid; proc_path /= "maps"; if (!std::filesystem::exists(proc_path, ec)) return ""; return proc_path.string(); } std::string get_proc_maps(pid_t pid) { return get_proc_maps(std::to_string(pid)); } std::vector get_mapped_paths_for_pid(pid_t pid) { static std::map> paths_cache; auto it = paths_cache.find(pid); if (it != paths_cache.end()) { return it->second; } std::vector paths; // start with the exe std::string pid_exe = get_pid_exe(pid); if (!pid_exe.empty() && pid_exe.find("(deleted)") == std::string::npos) paths.push_back(get_pid_exe(pid)); // get all the mapped libraries std::string maps_path = get_proc_maps(pid); if (maps_path.empty()) { LOG(WARNING) << "Maps path is empty"; return paths; } std::fstream fs(maps_path, std::ios_base::in); if (!fs.is_open()) { LOG(WARNING) << "Unable to open procfs mapfile: " << maps_path; return paths; } std::unordered_set seen_mappings; std::string line; // Example mapping: // 7fc8ee4fa000-7fc8ee4fb000 r--p 00000000 00:1f 27168296 /usr/libc.so.6 while (std::getline(fs, line)) { char buf[PATH_MAX + 1]; buf[0] = '\0'; auto res = std::sscanf(line.c_str(), "%*s %*s %*x %*s %*u %[^\n]", buf); // skip [heap], [vdso], and non file paths etc... if (res == 1 && buf[0] == '/') { std::string name = buf; if (name.find("(deleted)") == std::string::npos && seen_mappings.count(name) == 0) { seen_mappings.emplace(name); paths.push_back(std::move(name)); } } } paths_cache.emplace(pid, paths); return paths; } std::vector get_mapped_paths_for_running_pids() { std::unordered_set unique_paths; for (auto pid : get_all_running_pids()) { for (auto &path : get_mapped_paths_for_pid(pid)) { unique_paths.insert(std::move(path)); } } std::vector paths; for (auto &path : unique_paths) { paths.emplace_back(std::move(path)); } return paths; } bool has_wildcard(const std::string &str) { return str.find("*") != std::string::npos || (str.find("[") != std::string::npos && str.find("]") != std::string::npos); } std::vector split_string(const std::string &str, char delimiter, bool remove_empty) { std::vector elems; std::stringstream ss(str); std::string value; while (std::getline(ss, value, delimiter)) { if (remove_empty && value.empty()) continue; elems.push_back(value); } return elems; } /// Erase prefix up to the first colon (:) from str and return the prefix std::string erase_prefix(std::string &str) { std::string prefix = str.substr(0, str.find(':')); str.erase(0, prefix.length() + 1); return prefix; } void erase_parameter_list(std::string &demangled_name) { size_t args_start = std::string::npos; ssize_t stack = 0; // Look for the parenthesis closing the parameter list, then find // the matching parenthesis at the start of the parameter list... for (ssize_t it = demangled_name.find_last_of(')'); it >= 0; --it) { if (demangled_name[it] == ')') stack++; if (demangled_name[it] == '(') stack--; if (stack == 0) { args_start = it; break; } } // If we found the start of the parameter list, // remove the parameters from the match line. if (args_start != std::string::npos) demangled_name.resize(args_start); } bool wildcard_match(std::string_view str, const std::vector &tokens, bool start_wildcard, bool end_wildcard) { size_t next = 0; if (!start_wildcard) if (str.find(tokens[0], next) != next) return false; for (const std::string &token : tokens) { size_t found = str.find(token, next); if (found == std::string::npos) return false; next = found + token.length(); } if (!end_wildcard) if (str.length() != next) return false; 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"); } std::vector get_possible_cpus() { return read_cpu_range("/sys/devices/system/cpu/possible"); } int get_max_cpu_id() { // When booting, the kernel ensures CPUs are ordered from 0 -> N so there // are no gaps in possible CPUs. CPU ID is also u32 so this cast is safe const auto num_possible_cpus = static_cast( get_possible_cpus().size()); assert(num_possible_cpus > 0); // Using global scratch variables for big string usage looks like: // bounded_cpu_id = bpf_get_smp_processor_id() & MAX_CPU_ID // buf = global_var[bounded_cpu_id][slot_id] // We bound CPU ID to satisfy the BPF verifier on older kernels. We use an AND // instruction vs. LLVM umin function to reduce the number of jumps in BPF to // ensure we don't hit the complexity limit of 8192 jumps // // To bound using AND, we need to ensure NUM_POSSIBLE_CPUS is rounded up to // next nearest power of 2. return round_up_to_next_power_of_two(num_possible_cpus) - 1; } std::vector get_kernel_cflags(const char *uname_machine, const std::string &ksrc, const std::string &kobj, const KConfig &kconfig) { std::vector cflags; std::string arch = uname_machine; const char *archenv; if (!strncmp(uname_machine, "x86_64", 6)) { arch = "x86"; } else if (uname_machine[0] == 'i' && !strncmp(&uname_machine[2], "86", 2)) { arch = "x86"; } else if (!strncmp(uname_machine, "arm", 3)) { arch = "arm"; } else if (!strncmp(uname_machine, "sa110", 5)) { arch = "arm"; } else if (!strncmp(uname_machine, "s390x", 5)) { arch = "s390"; } else if (!strncmp(uname_machine, "parisc64", 8)) { arch = "parisc"; } else if (!strncmp(uname_machine, "ppc", 3)) { arch = "powerpc"; } else if (!strncmp(uname_machine, "mips", 4)) { arch = "mips"; } else if (!strncmp(uname_machine, "sh", 2)) { arch = "sh"; } else if (!strncmp(uname_machine, "aarch64", 7)) { arch = "arm64"; } else if (!strncmp(uname_machine, "loongarch", 9)) { arch = "loongarch"; } // If ARCH env is defined, use it over uname archenv = getenv("ARCH"); if (archenv) arch = std::string(archenv); cflags.push_back("-nostdinc"); cflags.push_back("-isystem"); cflags.push_back("/virtual/lib/clang/include"); // see linux/Makefile for $(LINUXINCLUDE) + $(USERINCLUDE) cflags.push_back("-I" + ksrc + "/arch/" + arch + "/include"); cflags.push_back("-I" + kobj + "/arch/" + arch + "/include/generated"); cflags.push_back("-I" + ksrc + "/include"); cflags.push_back("-I" + kobj + "/include"); cflags.push_back("-I" + ksrc + "/arch/" + arch + "/include/uapi"); cflags.push_back("-I" + kobj + "/arch/" + arch + "/include/generated/uapi"); cflags.push_back("-I" + ksrc + "/include/uapi"); cflags.push_back("-I" + kobj + "/include/generated/uapi"); cflags.push_back("-include"); cflags.push_back(ksrc + "/include/linux/kconfig.h"); cflags.push_back("-D__KERNEL__"); cflags.push_back("-D__BPF_TRACING__"); cflags.push_back("-D__HAVE_BUILTIN_BSWAP16__"); cflags.push_back("-D__HAVE_BUILTIN_BSWAP32__"); cflags.push_back("-D__HAVE_BUILTIN_BSWAP64__"); cflags.push_back("-DKBUILD_MODNAME=\"bpftrace\""); // If ARCH env variable is set, pass this along. if (archenv) cflags.push_back("-D__TARGET_ARCH_" + arch); if (arch == "arm") { // Required by several header files in arch/arm/include cflags.push_back("-D__LINUX_ARM_ARCH__=7"); } if (arch == "arm64") { // arm64 defines KASAN_SHADOW_SCALE_SHIFT in a Makefile instead of defining // it in a header file. Since we're not executing make, we need to set the // value manually (values are taken from arch/arm64/Makefile). if (kconfig.has_value("CONFIG_KASAN", "y")) { if (kconfig.has_value("CONFIG_KASAN_SW_TAGS", "y")) cflags.push_back("-DKASAN_SHADOW_SCALE_SHIFT=4"); else cflags.push_back("-DKASAN_SHADOW_SCALE_SHIFT=3"); } } 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::array, 2> get_cgroup_hierarchy_roots() { // Get all cgroup mounts and their type (cgroup/cgroup2) from /proc/mounts std::ifstream mounts_file("/proc/mounts"); std::array, 2> 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)) { if (std::filesystem::is_directory(match[2].str())) { if (match[1].str() == "cgroup") { result[0].push_back(match[2].str()); } else if (match[1].str() == "cgroup2") { result[1].push_back(match[2].str()); } } } } mounts_file.close(); return result; } std::vector> get_cgroup_paths( uint64_t cgroupid, std::string filter) { auto roots = get_cgroup_hierarchy_roots(); // Replace cgroup version with cgroup mount point directory name for cgroupv1 // roots and "unified" for cgroupv2 roots auto types_v1 = roots[0] | std::views::transform( [&](auto &root) -> std::pair { return { std::filesystem::path(root).filename().string(), root }; }); auto types_v2 = roots[1] | std::views::transform( [&](auto &root) -> std::pair { return { "unified", root }; }); // Filter roots bool start_wildcard, end_wildcard; auto tokens = get_wildcard_tokens(filter, start_wildcard, end_wildcard); auto filter_func = std::views::filter([&](auto pair) -> bool { return wildcard_match(pair.first, tokens, start_wildcard, end_wildcard); }); auto filtered_v1 = types_v1 | filter_func; auto filtered_v2 = types_v2 | filter_func; // Get cgroup path for each root auto get_path_func = std::views::transform( [&](auto pair) -> std::pair { return { pair.first, get_cgroup_path_in_hierarchy(cgroupid, pair.second) }; }); auto paths_v1 = filtered_v1 | get_path_func; auto paths_v2 = filtered_v2 | get_path_func; // Return paths with v2 first, then v1 sorted lexically by name. std::vector> sorted(paths_v2.begin(), paths_v2.end()); std::vector> sorted_v1(paths_v1.begin(), paths_v1.end()); std::sort(sorted_v1.begin(), sorted_v1.end()); sorted.insert(sorted.end(), sorted_v1.begin(), sorted_v1.end()); return sorted; } bool is_module_loaded(const std::string &module) { if (module == "vmlinux") { return true; } // This file lists all loaded modules std::ifstream modules_file("/proc/modules"); for (std::string line; std::getline(modules_file, line);) { if (line.compare(0, module.size() + 1, module + " ") == 0) { modules_file.close(); return true; } } modules_file.close(); return false; } bool is_dir(const std::string &path) { std::error_code ec; std::filesystem::path buf{ path }; return std::filesystem::is_directory(buf, ec); } // get_kernel_dirs fills {ksrc, kobj} - directories for pristine and // generated kernel sources - and returns if they were found. // // When the kernel was built in its source tree ksrc == kobj, however when // the kernel was build in a different directory than its source, ksrc != kobj. // // A notable example is Debian, which places pristine kernel headers in // // /lib/modules/`uname -r`/source/ // // and generated kernel headers in // // /lib/modules/`uname -r`/build/ // // false is returned if no trace of kernel headers was found at all, with the // guessed location set anyway for later warning. // // Both ksrc and kobj are guaranteed to be != "" bool get_kernel_dirs(const struct utsname &utsname, std::string &ksrc, std::string &kobj) { ksrc = kobj = std::string(KERNEL_HEADERS_DIR); if (!ksrc.empty()) return true; const char *kpath_env = ::getenv("BPFTRACE_KERNEL_SOURCE"); if (kpath_env) { ksrc = std::string(kpath_env); const char *kpath_build_env = ::getenv("BPFTRACE_KERNEL_BUILD"); if (kpath_build_env) { kobj = std::string(kpath_build_env); } else { kobj = ksrc; } return true; } std::string kdir = std::string("/lib/modules/") + utsname.release; ksrc = kdir + "/source"; kobj = kdir + "/build"; // if one of source/ or build/ is not present - try to use the other one for // both. auto has_ksrc = is_dir(ksrc); auto has_kobj = is_dir(kobj); if (!has_ksrc && !has_kobj) { return false; } if (!has_ksrc) { ksrc = kobj; } else if (!has_kobj) { kobj = ksrc; } return true; } const std::string &is_deprecated(const std::string &str) { for (auto &item : DEPRECATED_LIST) { if (!item.matches(str)) { continue; } if (item.show_warning) { LOG(WARNING) << item.old_name << " is deprecated and will be removed in the future. Use " << item.new_name << " instead."; item.show_warning = false; } if (item.replace_by_new_name) { return item.new_name; } else { return str; } } return str; } bool is_unsafe_func(const std::string &func_name) { return std::any_of(UNSAFE_BUILTIN_FUNCS.begin(), UNSAFE_BUILTIN_FUNCS.end(), [&](const auto &cand) { return func_name == cand; }); } bool is_compile_time_func(const std::string &func_name) { return std::any_of(COMPILE_TIME_FUNCS.begin(), COMPILE_TIME_FUNCS.end(), [&](const auto &cand) { return func_name == cand; }); } bool is_supported_lang(const std::string &lang) { return std::any_of(UPROBE_LANGS.begin(), UPROBE_LANGS.end(), [&](const auto &cand) { return lang == cand; }); } bool is_type_name(std::string_view str) { return str.find("struct ") == 0 || str.find("union ") == 0 || str.find("enum ") == 0; } std::string exec_system(const char *cmd) { std::array buffer; std::string result; std::shared_ptr pipe(popen(cmd, "r"), pclose); if (!pipe) throw bpftrace::FatalUserException("popen() failed!"); while (!feof(pipe.get())) { if (fgets(buffer.data(), 128, pipe.get()) != nullptr) result += buffer.data(); } return result; } // Original resolve_binary_path API defaulting to bpftrace's mount namespace std::vector resolve_binary_path(const std::string &cmd) { const char *env_paths = getenv("PATH"); return resolve_binary_path(cmd, env_paths, -1); } // If a pid is specified, the binary path is taken relative to its own PATH if // it is in a different mount namespace. Otherwise, the path is resolved // relative to the local PATH env var for bpftrace's own mount namespace if it // is set std::vector resolve_binary_path(const std::string &cmd, int pid) { std::string env_paths = ""; std::ostringstream pid_environ_path; if (pid > 0 && pid_in_different_mountns(pid)) { pid_environ_path << "/proc/" << pid << "/environ"; std::ifstream environ(pid_environ_path.str()); if (environ) { std::string env_var; std::string pathstr = ("PATH="); while (std::getline(environ, env_var, '\0')) { if (env_var.find(pathstr) != std::string::npos) { env_paths = env_var.substr(pathstr.length()); break; } } } return resolve_binary_path(cmd, env_paths.c_str(), pid); } else { return resolve_binary_path(cmd, getenv("PATH"), pid); } } // Check whether 'path' refers to a ELF file. Errors are swallowed silently and // result in return of 'nullopt'. On success, the ELF type (e.g., ET_DYN) is // returned. static std::optional is_elf(const std::string &path) { int fd; Elf *elf; GElf_Ehdr ehdr; if (elf_version(EV_CURRENT) == EV_NONE) { return std::nullopt; } fd = open(path.c_str(), O_RDONLY, 0); if (fd < 0) { return std::nullopt; } SCOPE_EXIT { ::close(fd); }; elf = elf_begin(fd, ELF_C_READ, nullptr); if (elf == nullptr) { return std::nullopt; } SCOPE_EXIT { ::elf_end(elf); }; if (elf_kind(elf) != ELF_K_ELF) { return std::nullopt; } if (!gelf_getehdr(elf, &ehdr)) { return std::nullopt; } return ehdr.e_type; } static bool has_exec_permission(const std::string &path) { using std::filesystem::perms; auto perms = std::filesystem::status(path).permissions(); return (perms & perms::owner_exec) != perms::none; } // Check whether 'path' refers to an executable ELF file. bool is_exe(const std::string &path) { if (auto e_type = is_elf(path)) { return e_type == ET_EXEC && has_exec_permission(path); } return false; } // Private interface to resolve_binary_path, used for the exposed variants // above, allowing for a PID whose mount namespace should be optionally // considered. static std::vector resolve_binary_path(const std::string &cmd, const char *env_paths, int pid) { std::vector candidate_paths = { cmd }; if (env_paths != nullptr && cmd.find("/") == std::string::npos) for (const auto &path : split_string(env_paths, ':')) candidate_paths.push_back(path + "/" + cmd); if (cmd.find("*") != std::string::npos) candidate_paths = ::expand_wildcard_paths(candidate_paths); std::vector valid_executable_paths; for (const auto &path : candidate_paths) { std::string rel_path; if (pid > 0 && pid_in_different_mountns(pid)) rel_path = path_for_pid_mountns(pid, path); else rel_path = path; // Both executables and shared objects are game. if (auto e_type = is_elf(rel_path)) { if ((e_type == ET_EXEC && has_exec_permission(rel_path)) || e_type == ET_DYN) { valid_executable_paths.push_back(rel_path); } } } return valid_executable_paths; } std::string path_for_pid_mountns(int pid, const std::string &path) { std::ostringstream pid_relative_path; char pid_root[64]; snprintf(pid_root, sizeof(pid_root), "/proc/%d/root", pid); if (path.find(pid_root) != 0) { std::string sep = (path.length() >= 1 && path.at(0) == '/') ? "" : "/"; pid_relative_path << pid_root << sep << path; } else { // The path is already relative to the pid's root pid_relative_path << path; } return pid_relative_path.str(); } // Determines if the target process is in a different mount namespace from // bpftrace. // // If a process is in a different mount namespace (eg, container) it is very // likely that any references to local paths will not be valid, and that paths // need to be made relative to the PID. // // If an invalid PID is specified or doesn't exist, it returns false. // True is only returned if the namespace of the target process could be read // and it doesn't match that of bpftrace. If there was an error reading either // mount namespace, it will throw an exception static bool pid_in_different_mountns(int pid) { if (pid <= 0) return false; std::error_code ec; std::filesystem::path self_path{ "/proc/self/ns/mnt" }; std::filesystem::path target_path{ "/proc" }; target_path /= std::to_string(pid); target_path /= "ns/mnt"; if (!std::filesystem::exists(self_path, ec)) { throw MountNSException( "Failed to compare mount ns with PID " + std::to_string(pid) + ". The error was open (/proc/self/ns/mnt): " + ec.message()); } if (!std::filesystem::exists(target_path, ec)) { throw MountNSException( "Failed to compare mount ns with PID " + std::to_string(pid) + ". The error was open (/proc//ns/mnt): " + ec.message()); } bool result = !std::filesystem::equivalent(self_path, target_path, ec); if (ec) { throw MountNSException("Failed to compare mount ns with PID " + std::to_string(pid) + ". The error was (fstat): " + ec.message()); } return result; } void cat_file(const char *filename, size_t max_bytes, std::ostream &out) { std::ifstream file(filename); const size_t BUFSIZE = 4096; if (file.fail()) { LOG(ERROR) << "failed to open file '" << filename << "': " << strerror(errno); return; } char buf[BUFSIZE]; size_t bytes_read = 0; // Read the file batches to avoid allocating a potentially // massive buffer. while (bytes_read < max_bytes) { size_t size = std::min(BUFSIZE, max_bytes - bytes_read); file.read(buf, size); out.write(buf, file.gcount()); if (file.eof()) { return; } if (file.fail()) { LOG(ERROR) << "failed to open file '" << filename << "': " << strerror(errno); return; } bytes_read += file.gcount(); } } std::string str_join(const std::vector &list, const std::string &delim) { std::string str; bool first = true; for (const auto &elem : list) { if (first) first = false; else str += delim; str += elem; } return str; } std::optional> get_int_from_str( const std::string &s) { if (s.size() == 0) { return std::nullopt; } if (s.starts_with("0x") || s.starts_with("0X")) { // Treat all hex's as unsigned std::size_t idx; try { uint64_t ret = std::stoull(s, &idx, 0); if (idx == s.size()) { return ret; } else { return std::nullopt; } } catch (...) { return std::nullopt; } } char *endptr; const char *s_ptr = s.c_str(); errno = 0; if (s.at(0) == '-') { int64_t ret = strtol(s_ptr, &endptr, 0); if (endptr == s_ptr || *endptr != '\0' || errno == ERANGE || errno == EINVAL) { return std::nullopt; } return ret; } uint64_t ret = strtoul(s_ptr, &endptr, 0); if (endptr == s_ptr || *endptr != '\0' || errno == ERANGE || errno == EINVAL) { return std::nullopt; } return ret; } bool symbol_has_cpp_mangled_signature(const std::string &sym_name) { if (!sym_name.rfind("_Z", 0) || !sym_name.rfind("____Z", 0)) return true; else return false; } static std::string get_invalid_pid_message(const std::string &pid, const std::string &msg) { return "pid '" + pid + "' " + msg; } std::optional parse_pid(const std::string &str, std::string &err) { std::size_t idx = 0; pid_t pid; constexpr ssize_t pid_max = 4 * 1024 * 1024; try { pid = std::stol(str, &idx, 10); } catch (const std::out_of_range &e) { err = get_invalid_pid_message(str, "outside of integer range"); return std::nullopt; } catch (const std::invalid_argument &e) { err = get_invalid_pid_message(str, "is not a valid decimal number"); return std::nullopt; } // Detect cases like `13ABC` if (idx < str.size()) { err = get_invalid_pid_message(str, "is not a valid decimal number"); return std::nullopt; } if (pid < 1 || pid > pid_max) { err = get_invalid_pid_message(str, "out of valid pid range [1," + std::to_string(pid_max) + "]"); return std::nullopt; } return pid; } std::string hex_format_buffer(const char *buf, size_t size, bool keep_ascii, bool escape_hex) { // Allow enough space for every byte to be sanitized in the form "\x00" std::string str(size * 4 + 1, '\0'); char *s = str.data(); size_t offset = 0; for (size_t i = 0; i < size; i++) if (keep_ascii && buf[i] >= 32 && buf[i] <= 126) offset += sprintf(s + offset, "%c", (reinterpret_cast(buf))[i]); else if (escape_hex) offset += sprintf(s + offset, "\\x%02x", (reinterpret_cast(buf))[i]); else offset += sprintf(s + offset, i == size - 1 ? "%02x" : "%02x ", (reinterpret_cast(buf))[i]); // Fit return value to actual length str.resize(offset); return str; } // Attaching to these kernel functions with fentry/fexit (kfunc/kretfunc) // could lead to a recursive loop and kernel crash so we need additional // generated BPF code to protect against this if one of these are being // attached to. bool is_recursive_func(const std::string &func_name) { return RECURSIVE_KERNEL_FUNCS.find(func_name) != RECURSIVE_KERNEL_FUNCS.end(); } static bool is_bad_func(std::string &func) { // Certain kernel functions are known to cause system stability issues if // traced (but not marked "notrace" in the kernel) so they should be filtered // out as the list is built. The list of functions have been taken from the // bpf kernel selftests (bpf/prog_tests/kprobe_multi_test.c). static const std::unordered_set bad_funcs = { "arch_cpu_idle", "default_idle", "bpf_dispatcher_xdp_func" }; static const std::vector bad_funcs_partial = { "__ftrace_invalid_address__", "rcu_" }; if (bad_funcs.find(func) != bad_funcs.end()) return true; for (const auto &s : bad_funcs_partial) { if (!std::strncmp(func.c_str(), s.c_str(), s.length())) return true; } return false; } FuncsModulesMap parse_traceable_funcs() { #ifdef FUZZ return {}; #else // Try to get the list of functions from BPFTRACE_AVAILABLE_FUNCTIONS_TEST env const char *path_env = std::getenv("BPFTRACE_AVAILABLE_FUNCTIONS_TEST"); const std::string kprobe_path = path_env ? path_env : tracefs::available_filter_functions(); std::ifstream available_funs(kprobe_path); if (available_funs.fail()) { LOG(V1) << "Error while reading traceable functions from " << kprobe_path << ": " << strerror(errno); return {}; } FuncsModulesMap result; std::string line; while (std::getline(available_funs, line)) { auto func_mod = split_symbol_module(line); if (func_mod.second.empty()) func_mod.second = "vmlinux"; if (!is_bad_func(func_mod.first)) result[func_mod.first].insert(func_mod.second); } // Filter out functions from the kprobe blacklist. const std::string kprobes_blacklist_path = debugfs::kprobes_blacklist(); std::ifstream kprobes_blacklist_funs(kprobes_blacklist_path); while (std::getline(kprobes_blacklist_funs, line)) { auto addr_func_mod = split_addrrange_symbol_module(line); if (result.find(std::get<1>(addr_func_mod)) != result.end()) { result.erase(std::get<1>(addr_func_mod)); } } return result; #endif } // Search for LINUX_VERSION_CODE in the vDSO, returning 0 if it can't be found. static uint32_t _find_version_note(unsigned long base) { auto ehdr = reinterpret_cast(base); for (int i = 0; i < ehdr->e_shnum; i++) { auto shdr = reinterpret_cast(base + ehdr->e_shoff + (i * ehdr->e_shentsize)); if (shdr->sh_type == SHT_NOTE) { auto ptr = reinterpret_cast(base + shdr->sh_offset); auto end = ptr + shdr->sh_size; while (ptr < end) { auto nhdr = reinterpret_cast(ptr); ptr += sizeof *nhdr; auto name = ptr; ptr += (nhdr->n_namesz + sizeof(ElfW(Word)) - 1) & -sizeof(ElfW(Word)); auto desc = ptr; ptr += (nhdr->n_descsz + sizeof(ElfW(Word)) - 1) & -sizeof(ElfW(Word)); if ((nhdr->n_namesz > 5 && !memcmp(name, "Linux", 5)) && nhdr->n_descsz == 4 && !nhdr->n_type) return *reinterpret_cast(desc); } } } return 0; } static uint32_t kernel_version_from_vdso() { // Fetch LINUX_VERSION_CODE from the vDSO .note section, falling back on // the build-time constant if unavailable. This always matches the // running kernel, but is not supported on arm32. unsigned code = 0; unsigned long base = getauxval(AT_SYSINFO_EHDR); if (base && !memcmp(reinterpret_cast(base), ELFMAG, 4)) code = _find_version_note(base); if (!code) code = LINUX_VERSION_CODE; return code; } static uint32_t kernel_version_from_uts() { struct utsname utsname; if (uname(&utsname) < 0) return 0; unsigned x, y, z; if (sscanf(utsname.release, "%u.%u.%u", &x, &y, &z) != 3) return 0; return KERNEL_VERSION(x, y, z); } static uint32_t kernel_version_from_khdr() { // Try to get the definition of LINUX_VERSION_CODE at runtime. std::ifstream linux_version_header{ "/usr/include/linux/version.h" }; const std::string content{ std::istreambuf_iterator( linux_version_header), std::istreambuf_iterator() }; const std::regex regex{ R"(#define\s+LINUX_VERSION_CODE\s+(\d+))" }; std::smatch match; if (std::regex_search(content.begin(), content.end(), match, regex)) return static_cast(std::stoi(match[1])); return 0; } // Find a LINUX_VERSION_CODE matching the host kernel. The build-time constant // may not match if bpftrace is compiled on a different Linux version than it's // used on, e.g. if built with Docker. uint32_t kernel_version(KernelVersionMethod method) { static std::optional a0, a1, a2; switch (method) { case vDSO: { if (!a0) a0 = kernel_version_from_vdso(); return *a0; } case UTS: { if (!a1) a1 = kernel_version_from_uts(); return *a1; } case File: { if (!a2) a2 = kernel_version_from_khdr(); return *a2; } case None: return 0; } // Unreachable return 0; } std::optional abs_path(const std::string &rel_path) { // filesystem::canonical does not work very well with /proc//root paths // of processes in a different mount namespace (than the one bpftrace is // running in), failing during canonicalization. See bpftrace:bpftrace#1595 static auto re = std::regex("^/proc/\\d+/root/.*"); if (!std::regex_match(rel_path, re)) { try { auto p = std::filesystem::path(rel_path); return std::filesystem::canonical(std::filesystem::absolute(p)).string(); } catch (std::filesystem::filesystem_error &) { return {}; } } else { return rel_path; } } bool symbol_has_module(const std::string &symbol) { return !symbol.empty() && symbol[symbol.size() - 1] == ']'; } std::pair split_symbol_module( const std::string &symbol) { if (!symbol_has_module(symbol)) return { symbol, "" }; size_t idx = symbol.rfind(" ["); if (idx == std::string::npos) return { symbol, "" }; return { symbol.substr(0, idx), symbol.substr(idx + strlen(" ["), symbol.length() - idx - strlen(" []")) }; } // Usually the /sys/kernel/debug/kprobes/blacklist file. // Format example: // 0xffffffff85201511-0xffffffff8520152f first_nmi // 0xffffffffc17e9373-0xffffffffc17e94ff vmx_vmexit [kvm_intel] // The outputs are: // { "0xffffffff85201511-0xffffffff8520152f", "first_nmi", "" } // { "0xffffffffc17e9373-0xffffffffc17e94ff", "vmx_vmexit", "kvm_intel" } std::tuple split_addrrange_symbol_module( const std::string &symbol) { size_t idx1 = symbol.rfind("\t"); size_t idx2 = symbol.rfind(" ["); if (idx2 == std::string::npos) return { symbol.substr(0, idx1), symbol.substr(idx1 + strlen("\t"), symbol.length() - idx1 - strlen("\t")), "" }; return { symbol.substr(0, idx1), symbol.substr(idx1 + strlen("\t"), idx2 - idx1 - strlen("\t")), symbol.substr(idx2 + strlen(" ["), symbol.length() - idx2 - strlen(" []")) }; } std::map> get_symbol_table_for_elf( const std::string &elf_file) { std::map> symbol_table; bcc_elf_symcb sym_resolve_callback = [](const char *name, uint64_t start, uint64_t length, void *payload) { auto *symbol_table = static_cast> *>(payload); symbol_table->insert({ start, { .name = std::string(name), .start = start, .end = start + length } }); return 0; }; struct bcc_symbol_option option; memset(&option, 0, sizeof(option)); option.use_symbol_type = BCC_SYM_ALL_TYPES ^ (1 << STT_NOTYPE); bcc_elf_foreach_sym( elf_file.c_str(), sym_resolve_callback, &option, &symbol_table); return symbol_table; } std::vector get_pids_for_program(const std::string &program) { std::error_code ec; auto program_abs = std::filesystem::canonical(program, ec); if (ec) { // std::filesystem::canonical will fail if we are attaching to a uprobe that // lives in another filesystem namespace. For example, // uprobe:/proc/12345/root/my_program:function1 // This shouldn't be a fatal condition as this function is only used to // attach to all running processes for a given binary, and the above uprobe // is targetting a specific process. So if this happens, just return no // pids. The probe will still attach directly to the targeted process. return {}; } std::vector pids; for (const auto &process : std::filesystem::directory_iterator("/proc")) { std::string filename = process.path().filename().string(); if (!std::all_of(filename.begin(), filename.end(), ::isdigit)) continue; std::error_code ec; std::filesystem::path pid_program = std::filesystem::read_symlink( process.path() / "exe", ec); if (!ec && program_abs == pid_program) pids.emplace_back(std::stoi(filename)); } return pids; } std::vector get_all_running_pids() { std::vector pids; for (const auto &process : std::filesystem::directory_iterator("/proc")) { std::string filename = process.path().filename().string(); if (!std::all_of(filename.begin(), filename.end(), ::isdigit)) continue; pids.emplace_back(std::stoi(filename)); } return pids; } // BPF verifier rejects programs with names containing certain characters, use // this function to replace every character not valid for C identifiers by '_' std::string sanitise_bpf_program_name(const std::string &name) { std::string sanitised_name = name; std::replace_if( sanitised_name.begin(), sanitised_name.end(), [](char c) { return !isalnum(c) && c != '_'; }, '_'); // Kernel KSYM_NAME_LEN is 128 until 6.1 // If we'll exceed the limit, hash the string and cap at 127 (+ null byte). if (sanitised_name.size() > 127) { size_t hash = std::hash{}(sanitised_name); // std::hash returns size_t, so we reserve 2*sizeof(size_t)+1 characters std::ostringstream os; os << sanitised_name.substr(0, 127 - (2 * sizeof(hash)) - 1) << '_' << std::setfill('0') << std::hex << hash; sanitised_name = os.str(); } return sanitised_name; } uint32_t round_up_to_next_power_of_two(uint32_t n) { // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 if (n == 0) { return 0; } // Note this function doesn't work if n > 2^31 since there are not enough // bits in unsigned 32 bit integers. This should be fine since its unlikely // anyone has > 2^31 CPUs. assert(n <= 2147483648); n--; n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; return n + 1; } } // namespace bpftrace bpftrace-0.23.2/src/utils.h000066400000000000000000000311171477746507000155100ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) namespace bpftrace { struct vmlinux_location { const char *path; // path with possible "%s" format to be replaced current // release bool raw; // file is either as ELF (false) or raw BTF data (true) }; extern const struct vmlinux_location vmlinux_locs[]; class MountNSException : public std::exception { public: MountNSException(const std::string &msg) : msg_(msg) { } const char *what() const noexcept override { return msg_.c_str(); } private: std::string msg_; }; class EnospcException : public std::runtime_error { public: // C++11 feature: bring base class constructor into scope to automatically // forward constructor calls to base class using std::runtime_error::runtime_error; }; // Use this to end bpftrace execution due to a user error. // These should be caught at a high level only e.g. main.cpp or bpftrace.cpp class FatalUserException : public std::runtime_error { public: // C++11 feature: bring base class constructor into scope to automatically // forward constructor calls to base class using std::runtime_error::runtime_error; }; class StdioSilencer { public: StdioSilencer() = default; ~StdioSilencer(); void silence(); protected: FILE *ofile; private: int old_stdio_ = -1; }; class StderrSilencer : public StdioSilencer { public: StderrSilencer() { ofile = stderr; } }; class StdoutSilencer : public StdioSilencer { public: StdoutSilencer() { ofile = stdout; } }; // Helper class to convert a pointer to an `std::istream` class Membuf : public std::streambuf { public: Membuf(uint8_t *begin, uint8_t *end) { auto b = reinterpret_cast(begin); auto e = reinterpret_cast(end); this->setg(b, b, e); } }; // Hack used to suppress build warning related to #474 template new_signature cast_signature(old_signature func) { #if __GNUC__ >= 8 _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wcast-function-type\"") #endif return reinterpret_cast(func); #if __GNUC__ >= 8 _Pragma("GCC diagnostic pop") #endif } struct DeprecatedName { std::string old_name; std::string new_name; bool show_warning = true; bool replace_by_new_name = true; bool matches(const std::string &name) const { // We allow a prefix match to match against builtins with number (argX) if (old_name.back() == '*') { std::string_view old_name_view{ old_name.c_str(), old_name.size() - 1 }; return name.rfind(old_name_view) == 0; } return name == old_name; } }; typedef std::unordered_map> FuncsModulesMap; struct KConfig { KConfig(); bool has_value(const std::string &name, const std::string &value) const { auto c = config.find(name); return c != config.end() && c->second == value; } std::unordered_map config; }; static std::vector DEPRECATED_LIST = { { "sarg*", "*(reg(\"sp\") + )", true, false } }; static std::vector UNSAFE_BUILTIN_FUNCS = { "system", "signal", "override", }; static std::vector COMPILE_TIME_FUNCS = { "cgroupid" }; static std::vector UPROBE_LANGS = { "cpp" }; static const std::set RECURSIVE_KERNEL_FUNCS = { "vmlinux:_raw_spin_lock", "vmlinux:_raw_spin_lock_irqsave", "vmlinux:_raw_spin_unlock_irqrestore", "vmlinux:queued_spin_lock_slowpath", }; void get_uint64_env_var(const ::std::string &str, const std::function &cb); void get_bool_env_var(const ::std::string &str, const std::function &cb); // Tries to find a file in $PATH std::optional find_in_path(std::string_view name); // Finds a file in the same directory as running binary std::optional find_near_self(std::string_view name); std::string get_pid_exe(pid_t pid); std::string get_pid_exe(const std::string &pid); std::string get_proc_maps(const std::string &pid); std::string get_proc_maps(pid_t pid); bool has_wildcard(const std::string &str); std::vector split_string(const std::string &str, char delimiter, bool remove_empty = false); std::string erase_prefix(std::string &str); void erase_parameter_list(std::string &demangled_name); bool wildcard_match(std::string_view str, const 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(); int get_max_cpu_id(); bool is_dir(const std::string &path); bool file_exists_and_ownedby_root(const char *f); std::optional find_vmlinux(struct symbol *sym = nullptr); bool get_kernel_dirs(const struct utsname &utsname, std::string &ksrc, std::string &kobj); std::vector get_kernel_cflags(const char *uname_machine, const std::string &ksrc, const std::string &kobj, const KConfig &kconfig); std::string get_cgroup_path_in_hierarchy(uint64_t cgroupid, std::string base_path); std::array, 2> get_cgroup_hierarchy_roots(); std::vector> get_cgroup_paths( uint64_t cgroupid, std::string filter); bool is_module_loaded(const std::string &module); FuncsModulesMap parse_traceable_funcs(); const std::string &is_deprecated(const std::string &str); bool is_recursive_func(const std::string &func_name); bool is_unsafe_func(const std::string &func_name); bool is_compile_time_func(const std::string &func_name); bool is_supported_lang(const std::string &lang); bool is_type_name(std::string_view str); std::string exec_system(const char *cmd); bool is_exe(const std::string &path); std::vector resolve_binary_path(const std::string &cmd); std::vector resolve_binary_path(const std::string &cmd, int pid); std::string path_for_pid_mountns(int pid, const std::string &path); void cat_file(const char *filename, size_t, std::ostream &); std::string str_join(const std::vector &list, const std::string &delim); std::optional> get_int_from_str( const std::string &s); bool symbol_has_cpp_mangled_signature(const std::string &sym_name); std::optional parse_pid(const std::string &str, std::string &err); std::string hex_format_buffer(const char *buf, size_t size, bool keep_ascii = true, bool escape_hex = true); std::optional abs_path(const std::string &rel_path); bool symbol_has_module(const std::string &symbol); std::pair split_symbol_module( const std::string &symbol); std::tuple split_addrrange_symbol_module( const std::string &symbol); std::vector get_mapped_paths_for_pid(pid_t pid); std::vector get_mapped_paths_for_running_pids(); struct elf_symbol { std::string name; uintptr_t start; uintptr_t end; }; // Get all symbols from an ELF module together with their address ranges in // the form of a map sorted by start address. // Note: the map uses std::greater as comparator to allow resolving of an // address inside a range using std::map::lower_bound. std::map> get_symbol_table_for_elf( const std::string &elf_file); std::vector get_pids_for_program(const std::string &program); std::vector get_all_running_pids(); uint32_t round_up_to_next_power_of_two(uint32_t n); std::string sanitise_bpf_program_name(const std::string &name); // Generate object file function name for a given probe inline std::string get_function_name_for_probe( const std::string &probe_name, int index, std::optional usdt_location_index = std::nullopt) { auto ret = sanitise_bpf_program_name(probe_name); if (usdt_location_index) ret += "_loc" + std::to_string(*usdt_location_index); ret += "_" + std::to_string(index); return ret; } inline std::string get_section_name(const std::string &function_name) { return "s_" + function_name; } inline std::string get_watchpoint_setup_probe_name( const std::string &probe_name) { return probe_name + "_wp_setup"; } inline std::string get_function_name_for_watchpoint_setup( const std::string &probe_name, int index) { return get_function_name_for_probe( get_watchpoint_setup_probe_name(probe_name), index); } // trim from end of string (right) inline std::string &rtrim(std::string &s) { s.erase(s.find_last_not_of(" ") + 1); return s; } // trim from beginning of string (left) inline std::string <rim(std::string &s) { s.erase(0, s.find_first_not_of(" ")); return s; } // trim from both ends of string (right then left) inline std::string &trim(std::string &s) { return ltrim(rtrim(s)); } template T read_data(const void *src) { T v; std::memcpy(&v, src, sizeof(v)); return v; } enum KernelVersionMethod { vDSO, UTS, File, None }; uint32_t kernel_version(KernelVersionMethod); template T reduce_value(const std::vector &value, int nvalues) { T sum = 0; for (int i = 0; i < nvalues; i++) { sum += read_data(value.data() + i * sizeof(T)); } return sum; } template T min_max_value(const std::vector &value, int nvalues, bool is_max) { T mm_val = 0; bool mm_set = false; for (int i = 0; i < nvalues; i++) { T val = read_data(value.data() + i * (sizeof(T) * 2)); uint32_t is_set = read_data(value.data() + sizeof(T) + i * (sizeof(T) * 2)); if (!is_set) { continue; } if (!mm_set) { mm_val = val; mm_set = true; } else if (is_max && val > mm_val) { mm_val = val; } else if (!is_max && val < mm_val) { mm_val = val; } } return mm_val; } template struct stats { T total; T count; T avg; }; template stats stats_value(const std::vector &value, int nvalues) { stats ret = { 0, 0, 0 }; for (int i = 0; i < nvalues; i++) { T val = read_data(value.data() + i * (sizeof(T) * 2)); T cpu_count = read_data(value.data() + sizeof(T) + i * (sizeof(T) * 2)); ret.count += cpu_count; ret.total += val; } ret.avg = (T)(ret.total / ret.count); return ret; } template T avg_value(const std::vector &value, int nvalues) { return stats_value(value, nvalues).avg; } // Combination of 2 hashes // The algorithm is taken from boost::hash_combine template inline void hash_combine(std::size_t &seed, const T &value) { std::hash hasher; seed ^= hasher(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } struct symbol { std::string name; uint64_t start; uint64_t size; uint64_t address; }; inline int sym_name_cb(const char *symname, uint64_t start, uint64_t size, void *p) { struct symbol *sym = static_cast(p); if (sym->name == symname) { sym->start = start; sym->size = size; return -1; } return 0; } inline int sym_address_cb(const char *symname, uint64_t start, uint64_t size, void *p) { struct symbol *sym = static_cast(p); // When size is 0, then [start, start + size) = [start, start) = ø. // So we need a special case when size=0, but address matches the symbol's if (sym->address == start || (sym->address > start && sym->address < (start + size))) { sym->start = start; sym->size = size; sym->name = symname; return -1; } return 0; } } // namespace bpftrace bpftrace-0.23.2/src/version.h.in000066400000000000000000000001521477746507000164350ustar00rootroot00000000000000#pragma once #include constexpr std::string_view BPFTRACE_VERSION = "@BPFTRACE_VERSION@"; bpftrace-0.23.2/tests/000077500000000000000000000000001477746507000145475ustar00rootroot00000000000000bpftrace-0.23.2/tests/CMakeLists.txt000066400000000000000000000114301477746507000173060ustar00rootroot00000000000000add_compile_options("-Werror=undef") # Regenerate codegen_includes.cpp whenever anything in the tests/codegen # directory changes file(GLOB_RECURSE CODEGEN_SOURCES CONFIGURE_DEPENDS codegen/*.cpp codegen/*.h) set(CODEGEN_INCLUDES_CPP codegen_includes.cpp) add_custom_command( OUTPUT ${CODEGEN_INCLUDES_CPP} COMMAND ${CMAKE_COMMAND} -DCODEGEN_INCLUDES_CPP="${CODEGEN_INCLUDES_CPP}" -DCODEGEN_SOURCES="${CODEGEN_SOURCES}" -P ${CMAKE_SOURCE_DIR}/tests/codegen/generate_codegen_includes.cmake DEPENDS ${CODEGEN_SOURCES}) # Codegen tests are based on the IR produced by LLVM 18. The compiler used to # build bpftrace is also important, as decisions on order of evaluation can # influence the order of the generated IR. if(${LLVM_VERSION_MAJOR} VERSION_EQUAL 18 AND ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") set(CODEGEN_SRC ${CODEGEN_INCLUDES_CPP}) else() set(CODEGEN_SRC "") message(STATUS "Disabled codegen tests. Requires GNU/GCC and LLVM 18 (found ${CMAKE_CXX_COMPILER_ID}, LLVM ${LLVM_VERSION_MAJOR})") endif() add_executable(bpftrace_test ast.cpp bpfbytecode.cpp bpftrace.cpp child.cpp clang_parser.cpp config.cpp collect_nodes.cpp cstring_view.cpp field_analyser.cpp function_registry.cpp log.cpp main.cpp mocks.cpp output.cpp parser.cpp portability_analyser.cpp procmon.cpp probe.cpp config_analyser.cpp resource_analyser.cpp required_resources.cpp return_path_analyser.cpp scopeguard.cpp semantic_analyser.cpp tracepoint_format_parser.cpp types.cpp utils.cpp ${CODEGEN_SRC} ) add_test(NAME bpftrace_test COMMAND bpftrace_test) add_subdirectory(data) if(${LLDB_FOUND}) add_dependencies(bpftrace_test debuginfo_dwarf_data) add_dependencies(bpftrace_test data_source_cxx) endif() add_dependencies(bpftrace_test debuginfo_btf_data) target_include_directories(bpftrace_test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(bpftrace_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(bpftrace_test PRIVATE TEST_CODEGEN_LOCATION="${CMAKE_SOURCE_DIR}/tests/codegen/llvm/") target_link_libraries(bpftrace_test libbpftrace) target_compile_definitions(bpftrace_test PRIVATE ${BPFTRACE_FLAGS}) if(BUILD_ASAN) target_compile_options(bpftrace_test PUBLIC "-fsanitize=address") target_link_options(bpftrace_test PUBLIC "-fsanitize=address") endif() # bpftrace tests require (at minimum) version 1.11. # There's no great way to enforce a minimum version from cmake -- the cmake # modules don't respect minimum requested version. find_package(GTest REQUIRED) find_package(GMock REQUIRED) include_directories(SYSTEM ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS}) target_link_libraries(bpftrace_test ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARIES}) find_package(Threads REQUIRED) target_link_libraries(bpftrace_test ${CMAKE_THREAD_LIBS_INIT}) add_subdirectory(testprogs) add_subdirectory(testlibs) # # Runtime Tests # configure_file(runtime-tests.sh runtime-tests.sh @ONLY) add_custom_target( runtime_tests COMMAND ./runtime-tests.sh DEPENDS testprogs testlibs ${CMAKE_BINARY_DIR}/src/bpftrace ) add_test(NAME runtime_tests COMMAND ./runtime-tests.sh) file(GLOB_RECURSE runtime_test_src_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS runtime/* ) list(REMOVE_ITEM runtime_test_src_files runtime/engine/cmake_vars.py) set(runtime_test_files) foreach(runtime_test_file ${runtime_test_src_files}) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${runtime_test_file} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${runtime_test_file} ${CMAKE_CURRENT_BINARY_DIR}/${runtime_test_file} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${runtime_test_file} ) list(APPEND runtime_test_files ${CMAKE_CURRENT_BINARY_DIR}/${runtime_test_file} ) endforeach() add_custom_target(runtime_test_files ALL DEPENDS ${runtime_test_files}) add_dependencies(runtime_tests runtime_test_files) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/runtime/engine) configure_file(runtime/engine/cmake_vars.py ${CMAKE_CURRENT_BINARY_DIR}/runtime/engine/ @ONLY) configure_file(tools-parsing-test.sh tools-parsing-test.sh COPYONLY) add_custom_target(tools-parsing-test COMMAND ./tools-parsing-test.sh) add_test(NAME tools-parsing-test COMMAND ./tools-parsing-test.sh) if(ENABLE_TEST_VALIDATE_CODEGEN) if(${LLVM_VERSION_MAJOR} VERSION_EQUAL 18) message(STATUS "Adding codegen-validator test") configure_file(codegen-validator.sh codegen-validator.sh COPYONLY) add_custom_target(codegen-validator COMMAND ./codegen-validator.sh) add_test(NAME codegen-validator COMMAND ./codegen-validator.sh ${CMAKE_SOURCE_DIR}) else() message(STATUS "Not building with LLVM 18, skipping codegen-validator test") endif() else() message(STATUS "codegen-validator test disabled") endif() bpftrace-0.23.2/tests/README.md000066400000000000000000000213661477746507000160360ustar00rootroot00000000000000# bpftrace Tests There are three different types of tests: [unit](#unit-tests), [runtime](#runtime-tests), and [tool parsing](#tool-parsing-tests). Every contribution should (1) not break the existing tests and (2) introduce new tests if relevant. ## Unit tests Unit tests for individual components (semantic analyser, codegen, etc.) are based on the GoogleTest framework. 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. These are located in `tests/*.cpp` and are executed by `/tests/bpftrace_test`. ### Codegen tests The codegen tests verify that the optimized IR matches our expectations. The tests are defined as C++ files in the `tests/codegen` directory and look like: ``` TEST(codegen, call_avg) { test("kprobe:f { @x = avg(pid) }", NAME); } ``` The `test` function does all the heavy lifting and is defined in `tests/codegen/common.h`. It compiles the specified program (first argument) and compares it (string compare) with the expected result, a file named by the second argument. The `NAME` macro holds the test name, which is `call_avg` in this case. These tests run as part of the normal suite of unit tests if you are running LLVM 18. If not, you need to install 'nix' and run these tests via this script: `./tests/codegen-tests.sh`. #### Updating **LLVM 18** If you are running LLVM 18 or want to only update specific tests with `--gtest_filter` run `/tests/bpftrace_test` with `BPFTRACE_UPDATE_TESTS=1` and the `test` helper will update the IR instead of running the tests. **Not LLVM 18** Run `./tests/codegen-tests.sh -u`. This updates all the codegen tests. ## Runtime tests Runtime tests will call the bpftrace executable. These are located in `tests/runtime` and are managed by a custom framework. * Run: `sudo make runtime_tests` inside your build directory or `sudo /tests/runtime-tests.sh` * Use the `TEST_FILTER` environment variable (or the `--filter` arg when running `runtime-tests.sh`) to only run a subset of the tests e.g. `TEST_FILTER="uprobe.*" sudo make runtime-tests` * There are environment variables to override paths for the bpftrace executables, if necessary. See runtime-tests.sh for details. Runtime tests are grouped into "suites". A suite is usually a single file. The name of the file is the name of the suite. ### Runtime test directives Each runtime testcase consists of multiple directives. In no particular order: * `NAME`: Name of the test case. This field is required. * `RUN`: Run the command in a shell. See "Runtime variables" below for available placeholders. This XOR the `PROG` field is required * `PROG`: Run the provided bpftrace program. This directive is preferred over `RUN` unless you must pass flags or create a shell pipeline. This XOR the `RUN` field is required. Multi-line program is supported by whitespace aligning subsequent lines to the beginning column of the first. * Example of multi-line program: ``` NAME multi-line PROG BEGIN { printf("hello ") } END { printf("world!\n") } EXPECT hello world! ``` * `EXPECT`: The expected output. Performs a literal match on an entire line of output. Multi-line EXPECT is supported by whitespace aligning subsequent lines to the beginning column of the first (same as `PROG`). * Example of multi-line EXPECT: ``` NAME multi-line PROG BEGIN { print("hello!"); print("world!") } EXPECT hello! world! ``` * `EXPECT_NONE`: The negation of `EXPECT`. * `EXPECT_REGEX`: A python regular expression to match the expected output. * `EXPECT_REGEX_NONE`: The negation of `EXPECT_REGEX`. * `EXPECT_FILE`: A file containing the expected output, matched as plain text after stripping initial and final empty lines * `EXPECT_JSON`: A json file containing the expected output, matched after converting the output and the file to a dict (thus ignoring field order). * `TIMEOUT`: The timeout for the testcase (in seconds). This field is required. * `BEFORE`: Run the command in a shell before running bpftrace. The command will run while bpftrace is running and be terminated after the test case finishes. Can be used multiple times, commands will run in parallel. * `AFTER`: Run the command in a shell after running bpftrace (after the probes are attached). The command will be terminated after the testcase is over. * `SETUP`: Run the command in a shell before the test is run. This differs from the `BEFORE` directive in that setup commands are expected to exit before bpftrace is executed. * `CLEANUP`: Run the command in a shell after test is over. This holds any cleanup command to free resources after test completes. * `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). `MAX_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). * `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. * `ARCH`: Only run testcase on provided architectures. Supports `|` to logical OR multiple arches. * `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) * `NEW_PIDNS`: This will execute the `BEFORE`, the bpftrace (`RUN` or `PROG`), and the `AFTER` commands in a new pid namespace that mounts proc. At least one `BEFORE` is required. * `SKIP_IF_ENV_HAS`: Skip test case if specified environment variable is found and matches value provided. Accepted format is KEY=VALUE. Only a single key/value pair per test is accepted. One or more [`EXPECT`, `EXPECT_NONE`, `EXPECT_REGEX`, `EXPECT_REGEX_NONE`] or a single [`EXPECT_FILE`, `EXPECT_JSON`] is required. 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. The BEFORE clause will block up to the TIMEOUT waiting for a PID matching the basename of the last space-separated token. For instance, if the BEFORE clause is `./testprogs/usdt_test`, it will wait for a processed called `usdt_test`. If it is `./testprogs/mountns_wrapper usdt_test` it will also wait for a process called `usdt_test`. This approach is invalidated if a test program requires arguments in the future, but so far test programs are simple and separate minimal programs to test tracing functionality, and argument passing hasn't been required. If test programs need arguments, a more sophisticated approach will be necessary. ### Runtime variables Runtime variables are placeholders that the runtime test engine will fill out before running the test. These exist b/c the values of the variables are generally not known until test time. The following runtime variables are available for the `RUN` directive: * `{{BPFTRACE}}`: Path to bpftrace executable * `{{BEFORE_PID}}`: Process ID of the process in `BEFORE` directive ### Test programs You can add test programs for your runtime tests by placing a `.c` or `.cpp` file corresponding to your test program in `tests/testprogs`. You can add test libraries for your runtime tests by placing a `.c` or `.cpp` file corresponding to your test library in `tests/testlibs`. The test file `tests/testprogs/my_test.c` will result in an executable that you can call and probe in your runtime test at `./testprogs/my_test` 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 Tools parsing tests run every tool in the `tools/` directory and ensure that the tools shipped with bpftrace are valid and can run. The validity of tools outputs is not checked at the moment. Tests can be executed by: `sudo /tests/tools-parsing-test.sh` ### 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. bpftrace-0.23.2/tests/ast.cpp000066400000000000000000000076031477746507000160500ustar00rootroot00000000000000#include "ast/ast.h" #include "gtest/gtest.h" namespace bpftrace::test::ast { using bpftrace::ast::AttachPoint; using bpftrace::ast::AttachPointList; using bpftrace::ast::Probe; TEST(ast, probe_name_special) { AttachPoint ap1("", false, location()); ap1.provider = "BEGIN"; Probe begin({ &ap1 }, nullptr, {}, location()); EXPECT_EQ(begin.name(), "BEGIN"); AttachPoint ap2("", false, location()); ap2.provider = "END"; Probe end({ &ap2 }, nullptr, {}, location()); EXPECT_EQ(end.name(), "END"); } TEST(ast, probe_name_kprobe) { AttachPoint ap1("", false, location()); ap1.provider = "kprobe"; ap1.func = "sys_read"; AttachPoint ap2("", false, location()); ap2.provider = "kprobe"; ap2.func = "sys_write"; AttachPoint ap3("", false, location()); ap3.provider = "kprobe"; ap3.func = "sys_read"; ap3.func_offset = 10; Probe kprobe1({ &ap1 }, nullptr, {}, location()); EXPECT_EQ(kprobe1.name(), "kprobe:sys_read"); Probe kprobe2({ &ap1, &ap2 }, nullptr, {}, location()); EXPECT_EQ(kprobe2.name(), "kprobe:sys_read,kprobe:sys_write"); Probe kprobe3({ &ap1, &ap2, &ap3 }, nullptr, {}, location()); EXPECT_EQ(kprobe3.name(), "kprobe:sys_read,kprobe:sys_write,kprobe:sys_read+10"); } TEST(ast, probe_name_uprobe) { AttachPoint ap1("", false, location()); ap1.provider = "uprobe"; ap1.target = "/bin/sh"; ap1.func = "readline"; Probe uprobe1({ &ap1 }, nullptr, {}, location()); EXPECT_EQ(uprobe1.name(), "uprobe:/bin/sh:readline"); AttachPoint ap2("", false, location()); ap2.provider = "uprobe"; ap2.target = "/bin/sh"; ap2.func = "somefunc"; Probe uprobe2({ &ap1, &ap2 }, nullptr, {}, location()); EXPECT_EQ(uprobe2.name(), "uprobe:/bin/sh:readline,uprobe:/bin/sh:somefunc"); AttachPoint ap3("", false, location()); ap3.provider = "uprobe"; ap3.target = "/bin/sh"; ap3.address = 1000; Probe uprobe3({ &ap1, &ap2, &ap3 }, nullptr, {}, location()); EXPECT_EQ( uprobe3.name(), "uprobe:/bin/sh:readline,uprobe:/bin/sh:somefunc,uprobe:/bin/sh:1000"); AttachPoint ap4("", false, location()); ap4.provider = "uprobe"; ap4.target = "/bin/sh"; ap4.func = "somefunc"; ap4.func_offset = 10; Probe uprobe4({ &ap1, &ap2, &ap3, &ap4 }, nullptr, {}, location()); EXPECT_EQ(uprobe4.name(), "uprobe:/bin/sh:readline,uprobe:/bin/sh:somefunc,uprobe:/bin/" "sh:1000,uprobe:/bin/sh:somefunc+10"); AttachPoint ap5("", false, location()); ap5.provider = "uprobe"; ap5.target = "/bin/sh"; ap5.address = 10; Probe uprobe5({ &ap5 }, nullptr, {}, location()); EXPECT_EQ(uprobe5.name(), "uprobe:/bin/sh:10"); AttachPoint ap6("", false, location()); ap6.provider = "uretprobe"; ap6.target = "/bin/sh"; ap6.address = 10; Probe uprobe6({ &ap6 }, nullptr, {}, location()); EXPECT_EQ(uprobe6.name(), "uretprobe:/bin/sh:10"); } TEST(ast, probe_name_usdt) { AttachPoint ap1("", false, location()); ap1.provider = "usdt"; ap1.target = "/bin/sh"; ap1.func = "probe1"; Probe usdt1({ &ap1 }, nullptr, {}, location()); EXPECT_EQ(usdt1.name(), "usdt:/bin/sh:probe1"); AttachPoint ap2("", false, location()); ap2.provider = "usdt"; ap2.target = "/bin/sh"; ap2.func = "probe2"; Probe usdt2({ &ap1, &ap2 }, nullptr, {}, location()); EXPECT_EQ(usdt2.name(), "usdt:/bin/sh:probe1,usdt:/bin/sh:probe2"); } TEST(ast, attach_point_name) { AttachPoint ap1("", false, location()); ap1.provider = "kprobe"; ap1.func = "sys_read"; AttachPoint ap2("", false, location()); ap2.provider = "kprobe"; ap2.func = "sys_thisone"; AttachPoint ap3("", false, location()); ap3.provider = "uprobe"; ap3.target = "/bin/sh"; ap3.func = "readline"; Probe kprobe({ &ap1, &ap2, &ap3 }, nullptr, {}, location()); EXPECT_EQ(ap2.name(), "kprobe:sys_thisone"); Probe uprobe({ &ap1, &ap2, &ap3 }, nullptr, {}, location()); EXPECT_EQ(ap3.name(), "uprobe:/bin/sh:readline"); } } // namespace bpftrace::test::ast bpftrace-0.23.2/tests/bpfbytecode.cpp000066400000000000000000000020051477746507000175360ustar00rootroot00000000000000#include "bpfbytecode.h" #include "ast/passes/codegen_llvm.h" #include "ast/passes/semantic_analyser.h" #include "driver.h" #include "mocks.h" #include "gtest/gtest.h" namespace bpftrace::test::bpfbytecode { BpfBytecode codegen(std::string_view input) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); EXPECT_EQ(driver.parse_str(input), 0); ast::SemanticAnalyser semantics(driver.ctx, *bpftrace); EXPECT_EQ(semantics.analyse(), 0); ast::CodegenLLVM codegen(driver.ctx, *bpftrace); return codegen.compile(); } TEST(bpfbytecode, create_programs) { auto bytecode = codegen("kprobe:foo { 1 }"); Probe foo; foo.type = ProbeType::kprobe; foo.name = "kprobe:foo"; foo.index = 1; auto &program = bytecode.getProgramForProbe(foo); EXPECT_EQ(std::string_view{ bpf_program__name(program.bpf_prog()) }, "kprobe_foo_1"); EXPECT_EQ(std::string_view{ bpf_program__section_name(program.bpf_prog()) }, "s_kprobe_foo_1"); } } // namespace bpftrace::test::bpfbytecode bpftrace-0.23.2/tests/bpftrace.cpp000066400000000000000000001223121477746507000170420ustar00rootroot00000000000000#include #include #include "ast/passes/codegen_llvm.h" #include "ast/passes/field_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpftrace.h" #include "clang_parser.h" #include "driver.h" #include "mocks.h" #include "tracefs.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace bpftrace::test::bpftrace { #include "btf_common.h" using ::testing::ContainerEq; using ::testing::StrictMock; static const int STRING_SIZE = 64; static const std::string kprobe_name(const std::string &attach_point, const std::string &target, uint64_t func_offset) { auto str = func_offset ? "+" + std::to_string(func_offset) : ""; if (!target.empty()) { return "kprobe:" + target + ":" + attach_point + str; } return "kprobe:" + attach_point + str; } static auto parse_probe(const std::string &str, BPFtrace &bpftrace, int usdt_num_locations = 0) { Driver driver(bpftrace); ASSERT_EQ(driver.parse_str(str), 0); ast::FieldAnalyser fields(driver.ctx, bpftrace); ASSERT_EQ(fields.analyse(), 0); ClangParser clang; clang.parse(driver.ctx.root, bpftrace); ast::SemanticAnalyser semantics(driver.ctx, bpftrace); ASSERT_EQ(semantics.analyse(), 0); auto usdt_helper = get_mock_usdt_helper(usdt_num_locations); std::stringstream out; ast::CodegenLLVM codegen(driver.ctx, bpftrace, std::move(usdt_helper)); codegen.generate_ir(); } void check_kprobe(Probe &p, const std::string &attach_point, const std::string &orig_name, uint64_t func_offset = 0, const std::string &target = "") { EXPECT_EQ(ProbeType::kprobe, p.type); EXPECT_EQ(attach_point, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ(kprobe_name(attach_point, target, func_offset), p.name); EXPECT_EQ(func_offset, p.func_offset); EXPECT_TRUE(p.funcs.empty()); } void check_kprobe_multi(Probe &p, const std::vector &funcs, const std::string &orig_name, const std::string &name) { EXPECT_EQ(ProbeType::kprobe, p.type); EXPECT_EQ(funcs, p.funcs); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ(name, p.name); } void check_uprobe(Probe &p, const std::string &path, const std::string &attach_point, const std::string &orig_name, const std::string &name, uint64_t address = 0, uint64_t func_offset = 0) { bool retprobe = orig_name.find("uretprobe:") == 0 || orig_name.find("ur:") == 0; EXPECT_EQ(retprobe ? ProbeType::uretprobe : ProbeType::uprobe, p.type); EXPECT_EQ(path, p.path); EXPECT_EQ(attach_point, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ(name, p.name); EXPECT_EQ(address, p.address); EXPECT_EQ(func_offset, p.func_offset); EXPECT_TRUE(p.funcs.empty()); } void check_uprobe_multi(Probe &p, const std::string &path, const std::vector &funcs, const std::string &orig_name, const std::string &name) { bool retprobe = orig_name.find("uretprobe:") == 0 || orig_name.find("ur:") == 0; EXPECT_EQ(retprobe ? ProbeType::uretprobe : ProbeType::uprobe, p.type); EXPECT_EQ(path, p.path); EXPECT_EQ(funcs, p.funcs); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ(name, p.name); } void check_usdt(Probe &p, const std::string &path, const std::string &provider, const std::string &attach_point, const std::string &orig_name) { EXPECT_EQ(ProbeType::usdt, p.type); EXPECT_EQ(attach_point, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("usdt:" + path + ":" + provider + ":" + attach_point, p.name); } void check_tracepoint(Probe &p, const std::string &target, const std::string &func, const std::string &orig_name) { EXPECT_EQ(ProbeType::tracepoint, p.type); EXPECT_EQ(func, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("tracepoint:" + target + ":" + func, p.name); } void check_rawtracepoint(Probe &p, const std::string &func, const std::string &orig_name) { EXPECT_EQ(ProbeType::rawtracepoint, p.type); EXPECT_EQ(func, p.attach_point); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("rawtracepoint:" + func, p.name); } void check_profile(Probe &p, const std::string &unit, int freq, const std::string &orig_name) { EXPECT_EQ(ProbeType::profile, p.type); EXPECT_EQ(freq, p.freq); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("profile:" + unit + ":" + std::to_string(freq), p.name); } void check_interval(Probe &p, const std::string &unit, int freq, const std::string &orig_name) { EXPECT_EQ(ProbeType::interval, p.type); EXPECT_EQ(freq, p.freq); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("interval:" + unit + ":" + std::to_string(freq), p.name); } void check_software(Probe &p, const std::string &unit, int freq, const std::string &orig_name) { EXPECT_EQ(ProbeType::software, p.type); EXPECT_EQ(freq, p.freq); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("software:" + unit + ":" + std::to_string(freq), p.name); } void check_hardware(Probe &p, const std::string &unit, int freq, const std::string &orig_name) { EXPECT_EQ(ProbeType::hardware, p.type); EXPECT_EQ(freq, p.freq); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ("hardware:" + unit + ":" + std::to_string(freq), p.name); } void check_special_probe(Probe &p, const std::string &orig_name) { EXPECT_EQ(ProbeType::special, p.type); EXPECT_EQ(orig_name, p.orig_name); EXPECT_EQ(orig_name, p.name); } TEST(bpftrace, add_begin_probe) { StrictMock bpftrace; parse_probe("BEGIN{}", bpftrace); ASSERT_EQ(0U, bpftrace.get_probes().size()); ASSERT_EQ(1U, bpftrace.get_special_probes().size()); check_special_probe(bpftrace.get_special_probes()["BEGIN"], "BEGIN"); } TEST(bpftrace, add_end_probe) { StrictMock bpftrace; parse_probe("END{}", bpftrace); ASSERT_EQ(0U, bpftrace.get_probes().size()); ASSERT_EQ(1U, bpftrace.get_special_probes().size()); check_special_probe(bpftrace.get_special_probes()["END"], "END"); } TEST(bpftrace, add_probes_single) { StrictMock bpftrace; parse_probe("kprobe:sys_read {}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_kprobe(bpftrace.get_probes().at(0), "sys_read", "kprobe:sys_read"); } TEST(bpftrace, add_probes_multiple) { StrictMock bpftrace; parse_probe("kprobe:sys_read,kprobe:sys_write{}", bpftrace); ASSERT_EQ(2U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read,kprobe:sys_write"; check_kprobe(bpftrace.get_probes().at(0), "sys_read", probe_orig_name); check_kprobe(bpftrace.get_probes().at(1), "sys_write", probe_orig_name); } TEST(bpftrace, add_probes_wildcard) { auto bpftrace = get_strict_mock_bpftrace(); bpftrace->feature_ = std::make_unique(false); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_traceable_funcs(false)) .Times(1); parse_probe("kprobe:sys_read,kprobe:my_*,kprobe:sys_write{}", *bpftrace); ASSERT_EQ(4U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read,kprobe:my_*,kprobe:sys_write"; check_kprobe(bpftrace->get_probes().at(0), "sys_read", probe_orig_name); check_kprobe(bpftrace->get_probes().at(1), "my_one", probe_orig_name); check_kprobe(bpftrace->get_probes().at(2), "my_two", probe_orig_name); check_kprobe(bpftrace->get_probes().at(3), "sys_write", probe_orig_name); } TEST(bpftrace, add_probes_wildcard_kprobe_multi) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_traceable_funcs(false)) .Times(2); parse_probe("kprobe:sys_read,kprobe:my_*,kprobe:sys_write{}", *bpftrace); ASSERT_EQ(3U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read,kprobe:my_*,kprobe:sys_write"; check_kprobe(bpftrace->get_probes().at(0), "sys_read", probe_orig_name); check_kprobe_multi(bpftrace->get_probes().at(1), { "my_one", "my_two" }, probe_orig_name, "kprobe:my_*"); check_kprobe(bpftrace->get_probes().at(2), "sys_write", probe_orig_name); } TEST(bpftrace, add_probes_wildcard_no_matches) { auto bpftrace = get_strict_mock_bpftrace(); bpftrace->feature_ = std::make_unique(false); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_traceable_funcs(false)) .Times(1); parse_probe("kprobe:sys_read,kprobe:not_here_*,kprobe:sys_write{}", *bpftrace); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read,kprobe:not_here_*,kprobe:sys_write"; check_kprobe(bpftrace->get_probes().at(0), "sys_read", probe_orig_name); check_kprobe(bpftrace->get_probes().at(1), "sys_write", probe_orig_name); } TEST(bpftrace, add_probes_wildcard_no_matches_kprobe_multi) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_traceable_funcs(false)) .Times(1); parse_probe("kprobe:sys_read,kprobe:not_here_*,kprobe:sys_write{}", *bpftrace); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read,kprobe:not_here_*,kprobe:sys_write"; check_kprobe(bpftrace->get_probes().at(0), "sys_read", probe_orig_name); check_kprobe(bpftrace->get_probes().at(1), "sys_write", probe_orig_name); } TEST(bpftrace, add_probes_kernel_module) { auto bpftrace = get_strict_mock_bpftrace(); parse_probe("kprobe:func_in_mod{}", *bpftrace); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:func_in_mod"; check_kprobe(bpftrace->get_probes().at(0), "func_in_mod", probe_orig_name); } TEST(bpftrace, add_probes_specify_kernel_module) { auto bpftrace = get_strict_mock_bpftrace(); parse_probe("kprobe:kernel_mod:func_in_mod{}", *bpftrace); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:kernel_mod:func_in_mod"; check_kprobe(bpftrace->get_probes().at(0), "func_in_mod", probe_orig_name, 0, "kernel_mod"); } TEST(bpftrace, add_probes_kernel_module_wildcard) { auto bpftrace = get_strict_mock_bpftrace(); // We enable kprobe_multi here but it doesn't support the module:function // syntax so full expansion should be done anyways. EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_traceable_funcs(true)) .Times(1); parse_probe("kprobe:*kernel_mod:* {}", *bpftrace); ASSERT_EQ(3U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:*kernel_mod:*"; check_kprobe(bpftrace->get_probes().at(0), "func_in_mod", probe_orig_name, 0, "kernel_mod"); check_kprobe(bpftrace->get_probes().at(1), "other_func_in_mod", probe_orig_name, 0, "kernel_mod"); check_kprobe(bpftrace->get_probes().at(2), "func_in_mod", probe_orig_name, 0, "other_kernel_mod"); } TEST(bpftrace, add_probes_kernel_module_function_wildcard) { auto bpftrace = get_strict_mock_bpftrace(); // We enable kprobe_multi here but it doesn't support the module:function // syntax so full expansion should be done anyways. EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_traceable_funcs(true)) .Times(1); parse_probe("kprobe:kernel_mod:*func_in_mod {}", *bpftrace); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "kprobe:kernel_mod:*func_in_mod"; check_kprobe(bpftrace->get_probes().at(0), "func_in_mod", probe_orig_name, 0, "kernel_mod"); check_kprobe(bpftrace->get_probes().at(1), "other_func_in_mod", probe_orig_name, 0, "kernel_mod"); } TEST(bpftrace, add_probes_offset) { auto offset = 10; StrictMock bpftrace; parse_probe("kprobe:sys_read+10{}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "kprobe:sys_read+" + std::to_string(offset); check_kprobe( bpftrace.get_probes().at(0), "sys_read", probe_orig_name, offset); } TEST(bpftrace, add_probes_uprobe) { StrictMock bpftrace; parse_probe("uprobe:/bin/sh:foo {}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_uprobe(bpftrace.get_probes().at(0), "/bin/sh", "foo", "uprobe:/bin/sh:foo", "uprobe:/bin/sh:foo"); } TEST(bpftrace, add_probes_uprobe_wildcard) { auto bpftrace = get_strict_mock_bpftrace(); bpftrace->feature_ = std::make_unique(false); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/sh")) .Times(1); parse_probe("uprobe:/bin/sh:*open {}", *bpftrace); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "uprobe:/bin/sh:*open"; check_uprobe(bpftrace->get_probes().at(0), "/bin/sh", "first_open", probe_orig_name, "uprobe:/bin/sh:first_open"); check_uprobe(bpftrace->get_probes().at(1), "/bin/sh", "second_open", probe_orig_name, "uprobe:/bin/sh:second_open"); } TEST(bpftrace, add_probes_uprobe_wildcard_uprobe_multi) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/sh")) .Times(2); parse_probe("uprobe:/bin/sh:*open {}", *bpftrace); std::string probe_orig_name = "uprobe:/bin/sh:*open"; ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_uprobe_multi(bpftrace->get_probes().at(0), "/bin/sh", { "/bin/sh:first_open", "/bin/sh:second_open" }, probe_orig_name, probe_orig_name); } TEST(bpftrace, add_probes_uprobe_wildcard_file) { auto bpftrace = get_strict_mock_bpftrace(); bpftrace->feature_ = std::make_unique(false); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/*sh")) .Times(1); parse_probe("uprobe:/bin/*sh:*open {}", *bpftrace); ASSERT_EQ(3U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "uprobe:/bin/*sh:*open"; check_uprobe(bpftrace->get_probes().at(0), "/bin/bash", "first_open", probe_orig_name, "uprobe:/bin/bash:first_open"); check_uprobe(bpftrace->get_probes().at(1), "/bin/sh", "first_open", probe_orig_name, "uprobe:/bin/sh:first_open"); check_uprobe(bpftrace->get_probes().at(2), "/bin/sh", "second_open", probe_orig_name, "uprobe:/bin/sh:second_open"); } TEST(bpftrace, add_probes_uprobe_wildcard_file_uprobe_multi) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/*sh")) .Times(2); parse_probe("uprobe:/bin/*sh:*open {}", *bpftrace); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "uprobe:/bin/*sh:*open"; check_uprobe_multi(bpftrace->get_probes().at(0), "/bin/sh", { "/bin/sh:first_open", "/bin/sh:second_open" }, probe_orig_name, "uprobe:/bin/sh:*open"); check_uprobe_multi(bpftrace->get_probes().at(1), "/bin/bash", { "/bin/bash:first_open" }, probe_orig_name, "uprobe:/bin/bash:*open"); } TEST(bpftrace, add_probes_uprobe_wildcard_no_matches) { auto bpftrace = get_strict_mock_bpftrace(); bpftrace->feature_ = std::make_unique(false); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/sh")) .Times(1); parse_probe("uprobe:/bin/sh:foo* {}", *bpftrace); ASSERT_EQ(0U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); } TEST(bpftrace, add_probes_uprobe_wildcard_no_matches_multi) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/sh")) .Times(1); parse_probe("uprobe:/bin/sh:foo* {}", *bpftrace); ASSERT_EQ(0U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); } TEST(bpftrace, add_probes_uprobe_address) { StrictMock bpftrace; parse_probe("uprobe:/bin/sh:1024 {}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_uprobe(bpftrace.get_probes().at(0), "/bin/sh", "", "uprobe:/bin/sh:1024", "uprobe:/bin/sh:1024", 1024); } TEST(bpftrace, add_probes_uprobe_string_offset) { StrictMock bpftrace; parse_probe("uprobe:/bin/sh:foo+10{}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_uprobe(bpftrace.get_probes().at(0), "/bin/sh", "foo", "uprobe:/bin/sh:foo+10", "uprobe:/bin/sh:foo+10", 0, 10); } TEST(bpftrace, add_probes_uprobe_cpp_symbol) { for (const std::string provider : { "uprobe", "uretprobe" }) { auto bpftrace = get_strict_mock_bpftrace(); bpftrace->feature_ = std::make_unique(false); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/sh")) .Times(1); std::string prog = provider + ":/bin/sh:cpp:cpp_mangled{}"; parse_probe(prog, *bpftrace); ASSERT_EQ(3U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_uprobe(bpftrace->get_probes().at(0), "/bin/sh", "_Z11cpp_mangledi", provider + ":/bin/sh:cpp:cpp_mangled", provider + ":/bin/sh:cpp:_Z11cpp_mangledi"); check_uprobe(bpftrace->get_probes().at(1), "/bin/sh", "_Z11cpp_mangledv", provider + ":/bin/sh:cpp:cpp_mangled", provider + ":/bin/sh:cpp:_Z11cpp_mangledv"); check_uprobe(bpftrace->get_probes().at(2), "/bin/sh", "cpp_mangled", provider + ":/bin/sh:cpp:cpp_mangled", provider + ":/bin/sh:cpp:cpp_mangled"); } } TEST(bpftrace, add_probes_uprobe_cpp_symbol_full) { auto bpftrace = get_strict_mock_bpftrace(); bpftrace->feature_ = std::make_unique(false); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/sh")) .Times(1); parse_probe("uprobe:/bin/sh:cpp:\"cpp_mangled(int)\"{}", *bpftrace); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_uprobe(bpftrace->get_probes().at(0), "/bin/sh", "_Z11cpp_mangledi", "uprobe:/bin/sh:cpp:cpp_mangled(int)", "uprobe:/bin/sh:cpp:_Z11cpp_mangledi"); } TEST(bpftrace, add_probes_uprobe_cpp_symbol_wildcard) { auto bpftrace = get_strict_mock_bpftrace(); bpftrace->feature_ = std::make_unique(false); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/sh")) .Times(1); parse_probe("uprobe:/bin/sh:cpp:cpp_man*{}", *bpftrace); ASSERT_EQ(4U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_uprobe(bpftrace->get_probes().at(0), "/bin/sh", "_Z11cpp_mangledi", "uprobe:/bin/sh:cpp:cpp_man*", "uprobe:/bin/sh:cpp:_Z11cpp_mangledi"); check_uprobe(bpftrace->get_probes().at(1), "/bin/sh", "_Z11cpp_mangledv", "uprobe:/bin/sh:cpp:cpp_man*", "uprobe:/bin/sh:cpp:_Z11cpp_mangledv"); check_uprobe(bpftrace->get_probes().at(2), "/bin/sh", "_Z18cpp_mangled_suffixv", "uprobe:/bin/sh:cpp:cpp_man*", "uprobe:/bin/sh:cpp:_Z18cpp_mangled_suffixv"); check_uprobe(bpftrace->get_probes().at(3), "/bin/sh", "cpp_mangled", "uprobe:/bin/sh:cpp:cpp_man*", "uprobe:/bin/sh:cpp:cpp_mangled"); } TEST(bpftrace, add_probes_uprobe_no_demangling) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_func_symbols_from_file(0, "/bin/sh")) .Times(0); // Without the :cpp prefix, only look for non-mangled "cpp_mangled" symbol parse_probe("uprobe:/bin/sh:cpp_mangled {}", *bpftrace); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_uprobe(bpftrace->get_probes().at(0), "/bin/sh", "cpp_mangled", "uprobe:/bin/sh:cpp_mangled", "uprobe:/bin/sh:cpp_mangled"); } #ifdef HAVE_LIBLLDB #include "dwarf_common.h" class bpftrace_dwarf : public test_dwarf {}; TEST_F(bpftrace_dwarf, add_probes_uprobe_symbol_source) { auto uprobe = "uprobe:" + std::string(bin_) + ":func_1 {}"; { BPFtrace bpftrace; ConfigSetter configs{ bpftrace.config_, ConfigSource::script }; configs.set_symbol_source_config("dwarf"); parse_probe(uprobe, bpftrace); ASSERT_EQ(bpftrace.resources.probes.size(), 1); ASSERT_EQ(bpftrace.resources.special_probes.size(), 0); auto &probe = bpftrace.resources.probes.at(0); ASSERT_TRUE(probe.attach_point.empty()); ASSERT_NE(probe.address, 0); } { BPFtrace bpftrace; ConfigSetter configs{ bpftrace.config_, ConfigSource::script }; configs.set_symbol_source_config("symbol_table"); parse_probe(uprobe, bpftrace); ASSERT_EQ(bpftrace.resources.probes.size(), 1); ASSERT_EQ(bpftrace.resources.special_probes.size(), 0); auto &probe = bpftrace.resources.probes.at(0); ASSERT_FALSE(probe.attach_point.empty()); ASSERT_EQ(probe.address, 0); } } #endif TEST(bpftrace, add_probes_usdt) { StrictMock bpftrace; parse_probe("usdt:/bin/sh:prov1:mytp {}", bpftrace, 1); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_usdt(bpftrace.get_probes().at(0), "/bin/sh", "prov1", "mytp", "usdt:/bin/sh:prov1:mytp"); } TEST(bpftrace, add_probes_usdt_wildcard) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_usdt(0, "/bin/*sh")) .Times(1); parse_probe("usdt:/bin/*sh:prov*:tp* {}", *bpftrace, 1); ASSERT_EQ(4U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); const std::string orig_name = "usdt:/bin/*sh:prov*:tp*"; check_usdt( bpftrace->get_probes().at(0), "/bin/bash", "prov1", "tp3", orig_name); check_usdt( bpftrace->get_probes().at(1), "/bin/sh", "prov1", "tp1", orig_name); check_usdt( bpftrace->get_probes().at(2), "/bin/sh", "prov1", "tp2", orig_name); check_usdt(bpftrace->get_probes().at(3), "/bin/sh", "prov2", "tp", orig_name); } TEST(bpftrace, add_probes_usdt_empty_namespace) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_usdt(0, "/bin/sh")) .Times(1); parse_probe("usdt:/bin/sh:tp1 {}", *bpftrace, 1); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_usdt(bpftrace->get_probes().at(0), "/bin/sh", "prov1", "tp1", "usdt:/bin/sh:tp1"); } TEST(bpftrace, add_probes_usdt_empty_namespace_conflict) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_usdt(0, "/bin/sh")) .Times(1); parse_probe("usdt:/bin/sh:tp {}", *bpftrace, 1); } TEST(bpftrace, add_probes_usdt_duplicate_markers) { auto bpftrace = get_strict_mock_bpftrace(); parse_probe("usdt:/bin/sh:prov1:mytp {}", *bpftrace, 3); ASSERT_EQ(3U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_usdt(bpftrace->get_probes().at(0), "/bin/sh", "prov1", "mytp", "usdt:/bin/sh:prov1:mytp"); check_usdt(bpftrace->get_probes().at(1), "/bin/sh", "prov1", "mytp", "usdt:/bin/sh:prov1:mytp"); check_usdt(bpftrace->get_probes().at(2), "/bin/sh", "prov1", "mytp", "usdt:/bin/sh:prov1:mytp"); } TEST(bpftrace, add_probes_tracepoint) { StrictMock bpftrace; parse_probe("tracepoint:sched:sched_switch {}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "tracepoint:sched:sched_switch"; check_tracepoint( bpftrace.get_probes().at(0), "sched", "sched_switch", probe_orig_name); } TEST(bpftrace, add_probes_tracepoint_wildcard) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file(tracefs::available_events())) .Times(1); parse_probe("tracepoint:sched:sched_* {}", *bpftrace); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "tracepoint:sched:sched_*"; check_tracepoint( bpftrace->get_probes().at(0), "sched", "sched_one", probe_orig_name); check_tracepoint( bpftrace->get_probes().at(1), "sched", "sched_two", probe_orig_name); } TEST(bpftrace, add_probes_tracepoint_category_wildcard) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file(tracefs::available_events())) .Times(1); parse_probe("tracepoint:sched*:sched_* {}", *bpftrace); ASSERT_EQ(3U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); std::string probe_orig_name = "tracepoint:sched*:sched_*"; check_tracepoint( bpftrace->get_probes().at(0), "sched", "sched_one", probe_orig_name); check_tracepoint( bpftrace->get_probes().at(1), "sched", "sched_two", probe_orig_name); check_tracepoint(bpftrace->get_probes().at(2), "sched_extra", "sched_extra", probe_orig_name); } TEST(bpftrace, add_probes_tracepoint_wildcard_no_matches) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file(tracefs::available_events())) .Times(1); parse_probe("tracepoint:type:typo_* {}", *bpftrace); ASSERT_EQ(0U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); } TEST(bpftrace, add_probes_profile) { StrictMock bpftrace; parse_probe("profile:ms:997 {}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "profile:ms:997"; check_profile(bpftrace.get_probes().at(0), "ms", 997, probe_orig_name); } TEST(bpftrace, add_probes_interval) { StrictMock bpftrace; parse_probe("i:s:1 {}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "interval:s:1"; check_interval(bpftrace.get_probes().at(0), "s", 1, probe_orig_name); } TEST(bpftrace, add_probes_software) { StrictMock bpftrace; parse_probe("software:faults:1000 {}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "software:faults:1000"; check_software(bpftrace.get_probes().at(0), "faults", 1000, probe_orig_name); } TEST(bpftrace, add_probes_hardware) { StrictMock bpftrace; parse_probe("hardware:cache-references:1000000 {}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "hardware:cache-references:1000000"; check_hardware(bpftrace.get_probes().at(0), "cache-references", 1000000, probe_orig_name); } 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; pair.first = std::vector(sizeof(uint64_t) * key.size()); pair.second = std::vector(sizeof(uint64_t)); uint8_t *key_data = pair.first.data(); uint8_t *val_data = pair.second.data(); for (size_t i = 0; i < key.size(); i++) { uint64_t k = key.at(i); std::memcpy(key_data + sizeof(uint64_t) * i, &k, sizeof(k)); } uint64_t v = val; std::memcpy(val_data, &v, sizeof(v)); return pair; } std::pair, std::vector> key_value_pair_str( std::vector key, int val) { std::pair, std::vector> pair; pair.first = std::vector(STRING_SIZE * key.size()); pair.second = std::vector(sizeof(uint64_t)); uint8_t *key_data = pair.first.data(); uint8_t *val_data = pair.second.data(); for (size_t i = 0; i < key.size(); i++) { strncpy(reinterpret_cast(key_data) + STRING_SIZE * i, key.at(i).c_str(), STRING_SIZE); } uint64_t v = val; std::memcpy(val_data, &v, sizeof(v)); return pair; } std::pair, std::vector> key_value_pair_int_str( int myint, std::string mystr, int val) { std::pair, std::vector> pair; pair.first = std::vector(sizeof(uint64_t) + STRING_SIZE); pair.second = std::vector(sizeof(uint64_t)); uint8_t *key_data = pair.first.data(); uint8_t *val_data = pair.second.data(); uint64_t k = myint, v = val; std::memcpy(key_data, &k, sizeof(k)); strncpy(reinterpret_cast(key_data) + sizeof(uint64_t), mystr.c_str(), STRING_SIZE); std::memcpy(val_data, &v, sizeof(v)); return pair; } TEST(bpftrace, sort_by_key_int) { StrictMock bpftrace; SizedType key_arg = CreateUInt64(); std::vector, std::vector>> values_by_key = { key_value_pair_int({ 2 }, 12), key_value_pair_int({ 3 }, 11), key_value_pair_int({ 1 }, 10), }; bpftrace.sort_by_key(key_arg, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_int({ 1 }, 10), key_value_pair_int({ 2 }, 12), key_value_pair_int({ 3 }, 11), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } TEST(bpftrace, sort_by_key_int_int) { StrictMock bpftrace; SizedType key = CreateTuple(bpftrace.structs.AddTuple( { CreateInt64(), CreateInt64(), CreateInt64() })); std::vector, std::vector>> values_by_key = { key_value_pair_int({ 5, 2, 1 }, 1), key_value_pair_int({ 5, 3, 1 }, 2), key_value_pair_int({ 5, 1, 1 }, 3), key_value_pair_int({ 2, 2, 2 }, 4), key_value_pair_int({ 2, 3, 2 }, 5), key_value_pair_int({ 2, 1, 2 }, 6), }; bpftrace.sort_by_key(key, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_int({ 2, 1, 2 }, 6), key_value_pair_int({ 2, 2, 2 }, 4), key_value_pair_int({ 2, 3, 2 }, 5), key_value_pair_int({ 5, 1, 1 }, 3), key_value_pair_int({ 5, 2, 1 }, 1), key_value_pair_int({ 5, 3, 1 }, 2), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } TEST(bpftrace, sort_by_key_str) { StrictMock bpftrace; SizedType key_arg = CreateString(STRING_SIZE); std::vector, std::vector>> values_by_key = { key_value_pair_str({ "z" }, 1), key_value_pair_str({ "a" }, 2), key_value_pair_str({ "x" }, 3), key_value_pair_str({ "d" }, 4), }; bpftrace.sort_by_key(key_arg, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_str({ "a" }, 2), key_value_pair_str({ "d" }, 4), key_value_pair_str({ "x" }, 3), key_value_pair_str({ "z" }, 1), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } TEST(bpftrace, sort_by_key_str_str) { StrictMock bpftrace; SizedType key = CreateTuple( bpftrace.structs.AddTuple({ CreateString(STRING_SIZE), CreateString(STRING_SIZE), CreateString(STRING_SIZE) })); std::vector, std::vector>> values_by_key = { key_value_pair_str({ "z", "a", "l" }, 1), key_value_pair_str({ "a", "a", "m" }, 2), key_value_pair_str({ "z", "c", "n" }, 3), key_value_pair_str({ "a", "c", "o" }, 4), key_value_pair_str({ "z", "b", "p" }, 5), key_value_pair_str({ "a", "b", "q" }, 6), }; bpftrace.sort_by_key(key, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_str({ "a", "a", "m" }, 2), key_value_pair_str({ "a", "b", "q" }, 6), key_value_pair_str({ "a", "c", "o" }, 4), key_value_pair_str({ "z", "a", "l" }, 1), key_value_pair_str({ "z", "b", "p" }, 5), key_value_pair_str({ "z", "c", "n" }, 3), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } TEST(bpftrace, sort_by_key_int_str) { StrictMock bpftrace; SizedType key = CreateTuple( bpftrace.structs.AddTuple({ CreateUInt64(), CreateString(STRING_SIZE) })); std::vector, std::vector>> values_by_key = { key_value_pair_int_str(1, "b", 1), key_value_pair_int_str(2, "b", 2), key_value_pair_int_str(3, "b", 3), key_value_pair_int_str(1, "a", 4), key_value_pair_int_str(2, "a", 5), key_value_pair_int_str(3, "a", 6), }; bpftrace.sort_by_key(key, values_by_key); std::vector, std::vector>> expected_values = { key_value_pair_int_str(1, "a", 4), key_value_pair_int_str(1, "b", 1), key_value_pair_int_str(2, "a", 5), key_value_pair_int_str(2, "b", 2), key_value_pair_int_str(3, "a", 6), key_value_pair_int_str(3, "b", 3), }; EXPECT_THAT(values_by_key, ContainerEq(expected_values)); } class bpftrace_btf : public test_btf {}; void check_probe(Probe &p, ProbeType type, const std::string &name) { EXPECT_EQ(type, p.type); EXPECT_EQ(name, p.name); } TEST_F(bpftrace_btf, add_probes_fentry) { auto bpftrace = get_strict_mock_bpftrace(); parse_probe("fentry:func_1,fexit:func_1 {}", *bpftrace); ASSERT_EQ(2U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_probe(bpftrace->get_probes().at(0), ProbeType::fentry, "fentry:mock_vmlinux:func_1"); check_probe(bpftrace->get_probes().at(1), ProbeType::fexit, "fexit:mock_vmlinux:func_1"); } TEST_F(bpftrace_btf, add_probes_kprobe) { StrictMock bpftrace; parse_probe("kprobe:mock_vmlinux:func_1,kretprobe:mock_vmlinux:func_1 {}", bpftrace); ASSERT_EQ(2U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); check_probe(bpftrace.get_probes().at(0), ProbeType::kprobe, "kprobe:mock_vmlinux:func_1"); check_probe(bpftrace.get_probes().at(1), ProbeType::kretprobe, "kretprobe:mock_vmlinux:func_1"); } TEST_F(bpftrace_btf, add_probes_iter_task) { auto bpftrace = get_strict_mock_bpftrace(); parse_probe("iter:task {}", *bpftrace); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_probe(bpftrace->get_probes().at(0), ProbeType::iter, "iter:task"); } TEST_F(bpftrace_btf, add_probes_iter_task_file) { auto bpftrace = get_strict_mock_bpftrace(); parse_probe("iter:task_file {}", *bpftrace); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_probe(bpftrace->get_probes().at(0), ProbeType::iter, "iter:task_file"); } TEST_F(bpftrace_btf, add_probes_iter_task_vma) { auto bpftrace = get_strict_mock_bpftrace(); parse_probe("iter:task_vma {}", *bpftrace); ASSERT_EQ(1U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); check_probe(bpftrace->get_probes().at(0), ProbeType::iter, "iter:task_vma"); } class bpftrace_bad_btf : public test_bad_btf {}; // Test that we can handle bad data and don't just crash TEST_F(bpftrace_bad_btf, parse_invalid_btf) { BPFtrace bpftrace; bpftrace.parse_btf({}); EXPECT_FALSE(bpftrace.has_btf_data()); } TEST(bpftrace, add_probes_rawtracepoint) { StrictMock bpftrace; parse_probe("rawtracepoint:sched_switch {}", bpftrace); ASSERT_EQ(1U, bpftrace.get_probes().size()); ASSERT_EQ(0U, bpftrace.get_special_probes().size()); std::string probe_orig_name = "rawtracepoint:sched_switch"; check_rawtracepoint(bpftrace.get_probes().at(0), "sched_switch", probe_orig_name); } TEST(bpftrace, add_probes_rawtracepoint_wildcard) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file(tracefs::available_events())) .Times(1); parse_probe(("rawtracepoint:sched_* {}"), *bpftrace); ASSERT_EQ(3U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); } TEST(bpftrace, add_probes_rawtracepoint_wildcard_no_matches) { auto bpftrace = get_strict_mock_bpftrace(); EXPECT_CALL(*bpftrace->mock_probe_matcher, get_symbols_from_file(tracefs::available_events())) .Times(1); parse_probe("rawtracepoint:typo_* {}", *bpftrace); ASSERT_EQ(0U, bpftrace->get_probes().size()); ASSERT_EQ(0U, bpftrace->get_special_probes().size()); } TEST(bpftrace, resolve_timestamp) { static const auto bootmode = static_cast(TimestampMode::boot); auto bpftrace = get_strict_mock_bpftrace(); // Basic sanity check bpftrace->boottime_ = { .tv_sec = 3, .tv_nsec = 0 }; bpftrace->resources.strftime_args.push_back("%s.%f"); EXPECT_EQ(bpftrace->resolve_timestamp(bootmode, 0, 1000), "3.000001"); // Check that boottime nsecs close to 1s doesn't trigger floating-point error. // // Due to the peculiarities of floating-point, not _any_ set of numbers // trigger the bug here. These values were discovered in the wild. bpftrace->boottime_ = { .tv_sec = 1736725826, .tv_nsec = 999999985 }; bpftrace->resources.strftime_args.push_back("%s"); EXPECT_EQ(bpftrace->resolve_timestamp(bootmode, 1, 0), "1736725826"); // Now check that we handle rollover to a new second correctly bpftrace->resources.strftime_args.push_back("%s.%f"); EXPECT_EQ(bpftrace->resolve_timestamp(bootmode, 2, 15), "1736725827.000000"); } } // namespace bpftrace::test::bpftrace bpftrace-0.23.2/tests/btf_common.h000066400000000000000000000042411477746507000170440ustar00rootroot00000000000000#pragma once #include #include #include "data/btf_data.h" namespace { constexpr std::array INVALID_BTF_DATA = { 0xDE, 0xAD, 0xBE, 0xEF }; bool create_tmp_with_data(char *path, const unsigned char *data, unsigned int data_len) { if (!path) return false; int fd = mkstemp(path); if (fd < 0) { std::remove(path); return false; } if (write(fd, data, data_len) != data_len) { close(fd); std::remove(path); return false; } close(fd); return true; } } // namespace class test_btf : public ::testing::Test { protected: void SetUp() override { // BTF data file char *btf_path = strdup("/tmp/btf_dataXXXXXX"); if (create_tmp_with_data(btf_path, btf_data, btf_data_len)) { setenv("BPFTRACE_BTF", btf_path, true); btf_path_ = btf_path; } // available functions file char *funcs_path = strdup("/tmp/available_filter_functionsXXXXXX"); if (create_tmp_with_data(funcs_path, func_list, func_list_len)) { setenv("BPFTRACE_AVAILABLE_FUNCTIONS_TEST", funcs_path, true); funcs_path_ = funcs_path; } } void TearDown() override { // clear the environment and remove the temp files unsetenv("BPFTRACE_BTF"); unsetenv("BPFTRACE_AVAILABLE_FUNCTIONS_TEST"); if (btf_path_) { std::remove(btf_path_); ::free(btf_path_); } if (funcs_path_) { std::remove(funcs_path_); ::free(funcs_path_); } } char *btf_path_ = nullptr; char *funcs_path_ = nullptr; }; class test_bad_btf : public ::testing::Test { protected: void SetUp() override { // BTF data file char *btf_path = strdup("/tmp/btf_dataXXXXXX"); if (create_tmp_with_data(btf_path, INVALID_BTF_DATA.data(), INVALID_BTF_DATA.size())) { setenv("BPFTRACE_BTF", btf_path, true); btf_path_ = btf_path; } } void TearDown() override { // clear the environment and remove the temp files unsetenv("BPFTRACE_BTF"); if (btf_path_) { std::remove(btf_path_); ::free(btf_path_); } } char *btf_path_ = nullptr; }; bpftrace-0.23.2/tests/child.cpp000066400000000000000000000146551477746507000163510ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "child.h" #include "childhelper.h" #include "utils.h" namespace bpftrace::test::child { using ::testing::HasSubstr; class childproc : public ::testing::Test { protected: void find(std::string &out, const char *path) { std::error_code ec; auto self = std::filesystem::read_symlink("/proc/self/exe", ec); ASSERT_FALSE(ec); auto parent_dir = self.parent_path(); out = parent_dir / std::filesystem::path(path); } void SetUp() override { find(TEST_BIN, "testprogs/true"); find(TEST_BIN_ERR, "testprogs/false"); find(TEST_BIN_SLOW, "testprogs/wait10"); } std::string TEST_BIN; std::string TEST_BIN_ERR; std::string TEST_BIN_SLOW; }; TEST_F(childproc, exe_does_not_exist) { try { ChildProc child("/does/not/exist/abc/fed"); FAIL(); } catch (const std::runtime_error &e) { EXPECT_THAT(e.what(), HasSubstr("does not exist or is not executable")); } } TEST_F(childproc, too_many_arguments) { std::stringstream cmd; cmd << TEST_BIN; for (int i = 0; i < 280; i++) cmd << " a"; try { ChildProc child(cmd.str()); FAIL(); } catch (const std::runtime_error &e) { EXPECT_THAT(e.what(), HasSubstr("Too many arguments")); } } TEST_F(childproc, child_exit_success) { // Spawn a child that exits successfully auto child = getChild(TEST_BIN); child->run(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), 0); EXPECT_EQ(child->term_signal(), -1); } TEST_F(childproc, child_exit_err) { // Spawn a child that exits with an error auto child = getChild(TEST_BIN_ERR); child->run(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_TRUE(child->exit_code() > 0); EXPECT_EQ(child->term_signal(), -1); } TEST_F(childproc, terminate) { auto child = getChild(TEST_BIN_SLOW); child->run(); child->terminate(); wait_for(child.get(), 100); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->term_signal(), SIGTERM); } TEST_F(childproc, destructor_destroy_child) { pid_t child_pid = 0; { std::unique_ptr child = getChild(TEST_BIN_SLOW); child->run(); child_pid = child->pid(); // Give child a little bit of time to execve before we kill it msleep(25); } int status = 0; pid_t ret = waitpid(child_pid, &status, WNOHANG); if (ret == -1 && errno == ECHILD) return; FAIL() << "Child should've been killed but appears to be alive: ret: " << ret << ", errno: " << errno << ", status: " << status << std::endl; } TEST_F(childproc, child_kill_before_exec) { signal(SIGHUP, SIG_DFL); auto child = getChild(TEST_BIN_SLOW); EXPECT_EQ(kill(child->pid(), SIGHUP), 0); wait_for(child.get(), 100); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), -1); EXPECT_EQ(child->term_signal(), SIGHUP); } TEST_F(childproc, stop_cont) { // STOP/CONT should not incorrectly mark the child // as dead auto child = getChild(TEST_BIN_SLOW); int status = 0; child->run(); msleep(25); EXPECT_TRUE(child->is_alive()); if (kill(child->pid(), SIGSTOP)) FAIL() << "kill(SIGSTOP)"; waitpid(child->pid(), &status, WUNTRACED); if (!(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP)) FAIL() << "! WIFSTOPPED"; EXPECT_TRUE(child->is_alive()); if (kill(child->pid(), SIGCONT)) FAIL() << "kill(SIGCONT)"; waitpid(child->pid(), &status, WCONTINUED); if (!WIFCONTINUED(status)) FAIL() << "! WIFCONTINUED"; EXPECT_TRUE(child->is_alive()); child->terminate(); wait_for(child.get(), 100); EXPECT_EQ(child->exit_code(), -1); EXPECT_EQ(child->term_signal(), SIGTERM); } TEST_F(childproc, ptrace_child_exit_success) { auto child = getChild(TEST_BIN); child->run(true); child->resume(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), 0); EXPECT_EQ(child->term_signal(), -1); } TEST_F(childproc, ptrace_child_exit_error) { auto child = getChild(TEST_BIN_ERR); child->run(true); child->resume(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_TRUE(child->exit_code() > 0); EXPECT_EQ(child->term_signal(), -1); } TEST_F(childproc, ptrace_child_kill_before_execve) { auto child = getChild(TEST_BIN); child->run(true); child->terminate(true); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), -1); EXPECT_EQ(child->term_signal(), 9); } TEST_F(childproc, ptrace_child_term_before_execve) { auto child = getChild(TEST_BIN); child->run(true); child->terminate(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->exit_code(), -1); EXPECT_EQ(child->term_signal(), 15); } TEST_F(childproc, multi_exec_match) { std::error_code ec; // Create directory for test std::string tmpdir = "/tmp/bpftrace-test-child-XXXXXX"; ASSERT_NE(::mkdtemp(&tmpdir[0]), nullptr); // Create fixture directories const auto path = std::filesystem::path(tmpdir); const auto usr_bin = path / "usr" / "bin"; ASSERT_TRUE(std::filesystem::create_directories(usr_bin, ec)); ASSERT_FALSE(ec); // Create symbolic link: bin -> usr/bin const auto symlink_bin = path / "bin"; std::filesystem::create_directory_symlink(usr_bin, symlink_bin, ec); ASSERT_FALSE(ec); // Copy a 'mysleep' binary and add x permission const auto binary = usr_bin / "mysleep"; { std::ifstream src; std::ofstream dst; src.open(TEST_BIN_SLOW, std::ios::in | std::ios::binary); dst.open(binary, std::ios::out | std::ios::binary); dst << src.rdbuf(); src.close(); dst.close(); EXPECT_EQ(::chmod(binary.c_str(), 0755), 0); } // Set ENV auto old_path = ::getenv("PATH"); auto new_path = usr_bin.native(); // copy new_path += ":"; new_path += symlink_bin.c_str(); EXPECT_EQ(::setenv("PATH", new_path.c_str(), 1), 0); // Use the filename with ambiguity. auto child = getChild(std::string(binary.filename())); child->run(); child->terminate(); wait_for(child.get(), 100); EXPECT_FALSE(child->is_alive()); EXPECT_EQ(child->term_signal(), SIGTERM); // Cleanup EXPECT_EQ(::setenv("PATH", old_path, 1), 0); EXPECT_GT(std::filesystem::remove_all(tmpdir), 0); } } // namespace bpftrace::test::child bpftrace-0.23.2/tests/childhelper.h000066400000000000000000000015541477746507000172100ustar00rootroot00000000000000#include #include "child.h" #include "utils.h" namespace bpftrace { namespace test { static int msleep(int msec) { struct timespec sleep = { .tv_sec = 0, .tv_nsec = msec * 1000000L }; struct timespec rem = {}; if (nanosleep(&sleep, &rem) < 0) return 1000L * rem.tv_sec + 1000000L * rem.tv_nsec; return 0; } static void wait_for(ChildProcBase *child, int msec_timeout) { constexpr int wait = 10; while (child->is_alive() && msec_timeout > 0) msec_timeout -= wait - msleep(wait); } static std::unique_ptr getChild(std::string cmd) { std::unique_ptr child; { StderrSilencer es; StdoutSilencer os; os.silence(); es.silence(); child = std::make_unique(cmd); } EXPECT_NE(child->pid(), -1); EXPECT_TRUE(child->is_alive()); return child; } } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/clang_parser.cpp000066400000000000000000000737371477746507000177340ustar00rootroot00000000000000#include "clang_parser.h" #include #include #include "ast/passes/field_analyser.h" #include "bpftrace.h" #include "driver.h" #include "struct.h" #include "gtest/gtest.h" #include namespace bpftrace::test::clang_parser { #include "btf_common.h" static void parse(const std::string &input, BPFtrace &bpftrace, bool result = true, const std::string &probe = "kprobe:sys_read { 1 }") { auto extended_input = input + probe; Driver driver(bpftrace); ASSERT_EQ(driver.parse_str(extended_input), 0); ast::FieldAnalyser fields(driver.ctx, bpftrace); EXPECT_EQ(fields.analyse(), 0); ClangParser clang; ASSERT_EQ(clang.parse(driver.ctx.root, bpftrace), result); } TEST(clang_parser, integers) { BPFtrace bpftrace; parse("struct Foo { int x; int y, z; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 12); ASSERT_EQ(foo->fields.size(), 3U); ASSERT_EQ(foo->HasField("x"), true); ASSERT_EQ(foo->HasField("y"), true); ASSERT_EQ(foo->HasField("z"), true); EXPECT_TRUE(foo->GetField("x").type.IsIntTy()); EXPECT_EQ(foo->GetField("x").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("x").offset, 0); EXPECT_TRUE(foo->GetField("y").type.IsIntTy()); EXPECT_EQ(foo->GetField("y").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("y").offset, 4); EXPECT_TRUE(foo->GetField("z").type.IsIntTy()); EXPECT_EQ(foo->GetField("z").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("z").offset, 8); } TEST(clang_parser, c_union) { BPFtrace bpftrace; parse("union Foo { char c; short s; int i; long l; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("union Foo")); auto foo = bpftrace.structs.Lookup("union Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 4U); ASSERT_TRUE(foo->HasField("c")); ASSERT_TRUE(foo->HasField("s")); ASSERT_TRUE(foo->HasField("i")); ASSERT_TRUE(foo->HasField("l")); EXPECT_TRUE(foo->GetField("c").type.IsIntTy()); EXPECT_EQ(foo->GetField("c").type.GetSize(), 1U); EXPECT_EQ(foo->GetField("c").offset, 0); EXPECT_TRUE(foo->GetField("s").type.IsIntTy()); EXPECT_EQ(foo->GetField("s").type.GetSize(), 2U); EXPECT_EQ(foo->GetField("s").offset, 0); EXPECT_TRUE(foo->GetField("i").type.IsIntTy()); EXPECT_EQ(foo->GetField("i").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("i").offset, 0); EXPECT_TRUE(foo->GetField("l").type.IsIntTy()); EXPECT_EQ(foo->GetField("l").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("l").offset, 0); } TEST(clang_parser, c_enum) { BPFtrace bpftrace; parse("enum E { NONE, SOME = 99, }; struct Foo { enum E e; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 4); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("e")); EXPECT_TRUE(foo->GetField("e").type.IsIntTy()); EXPECT_EQ(foo->GetField("e").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("e").offset, 0); ASSERT_TRUE(bpftrace.enums_.contains("NONE")); EXPECT_EQ(std::get<0>(bpftrace.enums_["NONE"]), 0); EXPECT_EQ(std::get<1>(bpftrace.enums_["NONE"]), "E"); ASSERT_TRUE(bpftrace.enums_.contains("SOME")); EXPECT_EQ(std::get<0>(bpftrace.enums_["SOME"]), 99); EXPECT_EQ(std::get<1>(bpftrace.enums_["SOME"]), "E"); ASSERT_TRUE(bpftrace.enum_defs_.contains("E")); ASSERT_TRUE(bpftrace.enum_defs_["E"].contains(0)); EXPECT_EQ(bpftrace.enum_defs_["E"][0], "NONE"); ASSERT_TRUE(bpftrace.enum_defs_["E"].contains(99)); EXPECT_EQ(bpftrace.enum_defs_["E"][99], "SOME"); } TEST(clang_parser, c_enum_anonymous) { BPFtrace bpftrace; parse( "enum { ANON_A_VARIANT_1 = 0, ANON_A_VARIANT_2, ANON_A_CONFLICT = 99, }; " "enum { ANON_B_VARIANT_1 = 0, ANON_B_CONFLICT = 99, }; ", bpftrace); ASSERT_EQ(bpftrace.enums_.size(), 5); ASSERT_EQ(bpftrace.enum_defs_.size(), 2); // // Check enums_ contains first anonymous enum // // Check first variant present ASSERT_TRUE(bpftrace.enums_.contains("ANON_A_VARIANT_1")); EXPECT_EQ(std::get<0>(bpftrace.enums_["ANON_A_VARIANT_1"]), 0); auto anon_a_name = std::get<1>(bpftrace.enums_["ANON_A_VARIANT_1"]); ASSERT_FALSE(anon_a_name.empty()); // Check second variant present ASSERT_TRUE(bpftrace.enums_.contains("ANON_A_VARIANT_2")); EXPECT_EQ(std::get<0>(bpftrace.enums_["ANON_A_VARIANT_2"]), 1); EXPECT_EQ(std::get<1>(bpftrace.enums_["ANON_A_CONFLICT"]), anon_a_name); // Check conflict variant present ASSERT_TRUE(bpftrace.enums_.contains("ANON_A_CONFLICT")); EXPECT_EQ(std::get<0>(bpftrace.enums_["ANON_A_CONFLICT"]), 99); EXPECT_EQ(std::get<1>(bpftrace.enums_["ANON_A_CONFLICT"]), anon_a_name); // // Check enum_defs_ contains first anonymous enum, with ANON_A_CONFLICT // value resolving correctly to the this enum and not the other. // ASSERT_TRUE(bpftrace.enum_defs_.contains(anon_a_name)); ASSERT_EQ(bpftrace.enum_defs_[anon_a_name].size(), 3); ASSERT_TRUE(bpftrace.enum_defs_[anon_a_name].contains(0)); EXPECT_EQ(bpftrace.enum_defs_[anon_a_name][0], "ANON_A_VARIANT_1"); ASSERT_TRUE(bpftrace.enum_defs_[anon_a_name].contains(1)); EXPECT_EQ(bpftrace.enum_defs_[anon_a_name][1], "ANON_A_VARIANT_2"); ASSERT_TRUE(bpftrace.enum_defs_[anon_a_name].contains(99)); EXPECT_EQ(bpftrace.enum_defs_[anon_a_name][99], "ANON_A_CONFLICT"); // // Check enums_ contains second anonymous enum // // Check first variant present ASSERT_TRUE(bpftrace.enums_.contains("ANON_B_VARIANT_1")); EXPECT_EQ(std::get<0>(bpftrace.enums_["ANON_B_VARIANT_1"]), 0); auto anon_b_name = std::get<1>(bpftrace.enums_["ANON_B_VARIANT_1"]); ASSERT_FALSE(anon_b_name.empty()); // Check conflict variant present ASSERT_TRUE(bpftrace.enums_.contains("ANON_B_CONFLICT")); EXPECT_EQ(std::get<0>(bpftrace.enums_["ANON_B_CONFLICT"]), 99); EXPECT_EQ(std::get<1>(bpftrace.enums_["ANON_B_CONFLICT"]), anon_b_name); // // Check enum_defs_ contains second anonymous enum, with ANON_B_CONFLICT // value resolving correctly to the this enum and not the first. // ASSERT_TRUE(bpftrace.enum_defs_.contains(anon_b_name)); ASSERT_EQ(bpftrace.enum_defs_[anon_b_name].size(), 2); ASSERT_TRUE(bpftrace.enum_defs_[anon_b_name].contains(0)); EXPECT_EQ(bpftrace.enum_defs_[anon_b_name][0], "ANON_B_VARIANT_1"); ASSERT_TRUE(bpftrace.enum_defs_[anon_b_name].contains(99)); EXPECT_EQ(bpftrace.enum_defs_[anon_b_name][99], "ANON_B_CONFLICT"); } TEST(clang_parser, integer_ptr) { BPFtrace bpftrace; parse("struct Foo { int *x; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("x")); EXPECT_TRUE(foo->GetField("x").type.IsPtrTy()); EXPECT_EQ(foo->GetField("x").type.GetPointeeTy()->GetIntBitWidth(), 8 * sizeof(int)); EXPECT_EQ(foo->GetField("x").offset, 0); } TEST(clang_parser, string_ptr) { BPFtrace bpftrace; parse("struct Foo { char *str; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("str")); auto &ty = foo->GetField("str").type; auto *pointee = ty.GetPointeeTy(); EXPECT_TRUE(pointee->IsIntTy()); EXPECT_EQ(pointee->GetIntBitWidth(), 8 * sizeof(char)); EXPECT_EQ(foo->GetField("str").offset, 0); } TEST(clang_parser, string_array) { BPFtrace bpftrace; parse("struct Foo { char str[32]; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 32); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("str")); EXPECT_TRUE(foo->GetField("str").type.IsStringTy()); EXPECT_EQ(foo->GetField("str").type.GetSize(), 32U); EXPECT_EQ(foo->GetField("str").offset, 0); } TEST(clang_parser, nested_struct_named) { BPFtrace bpftrace; parse("struct Bar { int x; } struct Foo { struct Bar bar; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); ASSERT_TRUE(bpftrace.structs.Has("struct Bar")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 4); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("bar")); auto &bar = foo->GetField("bar"); EXPECT_TRUE(bar.type.IsRecordTy()); EXPECT_EQ(bar.type.GetName(), "struct Bar"); EXPECT_EQ(bar.type.GetSize(), 4U); EXPECT_EQ(bar.offset, 0); } TEST(clang_parser, nested_struct_ptr_named) { BPFtrace bpftrace; parse("struct Bar { int x; } struct Foo { struct Bar *bar; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); ASSERT_TRUE(bpftrace.structs.Has("struct Bar")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("bar")); auto &bar = foo->GetField("bar"); EXPECT_TRUE(bar.type.IsPtrTy()); EXPECT_TRUE(bar.type.GetPointeeTy()->IsRecordTy()); EXPECT_EQ(bar.type.GetPointeeTy()->GetName(), "struct Bar"); EXPECT_EQ(bar.type.GetPointeeTy()->GetSize(), sizeof(int)); EXPECT_EQ(bar.offset, 0); } TEST(clang_parser, nested_struct_no_type) { BPFtrace bpftrace; // bar and baz's struct/union do not have type names, but are not anonymous // since they are called bar and baz parse("struct Foo { struct { int x; } bar; union { int y; } baz; }", bpftrace); std::string bar_name = "struct Foo::(unnamed at definitions.h:2:14)"; std::string baz_name = "union Foo::(unnamed at definitions.h:2:37)"; ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); ASSERT_TRUE(bpftrace.structs.Has(bar_name)); ASSERT_TRUE(bpftrace.structs.Has(baz_name)); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); auto bar = bpftrace.structs.Lookup(bar_name).lock(); auto baz = bpftrace.structs.Lookup(baz_name).lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 2U); ASSERT_TRUE(foo->HasField("bar")); ASSERT_TRUE(foo->HasField("baz")); EXPECT_EQ(bar->size, 4); ASSERT_EQ(bar->fields.size(), 1U); ASSERT_TRUE(bar->HasField("x")); EXPECT_TRUE(bar->GetField("x").type.IsIntTy()); EXPECT_EQ(bar->GetField("x").type.GetSize(), 4U); EXPECT_EQ(bar->GetField("x").offset, 0); EXPECT_EQ(baz->size, 4); ASSERT_EQ(baz->fields.size(), 1U); ASSERT_TRUE(baz->HasField("y")); EXPECT_TRUE(baz->GetField("y").type.IsIntTy()); EXPECT_EQ(baz->GetField("y").type.GetSize(), 4U); EXPECT_EQ(baz->GetField("y").offset, 0); { auto &bar_field = foo->GetField("bar"); EXPECT_TRUE(bar_field.type.IsRecordTy()); EXPECT_EQ(bar_field.type.GetSize(), sizeof(int)); EXPECT_EQ(bar_field.offset, 0); auto &baz_field = foo->GetField("baz"); EXPECT_TRUE(baz_field.type.IsRecordTy()); EXPECT_EQ(baz_field.type.GetSize(), sizeof(int)); EXPECT_EQ(baz_field.offset, 4); } } TEST(clang_parser, nested_struct_unnamed_fields) { BPFtrace bpftrace; parse("struct Foo" "{" " struct { int x; int y; };" // Anonymous struct field " int a;" " struct Bar { int z; };" // Struct definition - not a field of Foo "}", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); ASSERT_TRUE(bpftrace.structs.Has("struct Bar")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); auto bar = bpftrace.structs.Lookup("struct Bar").lock(); EXPECT_EQ(foo->size, 12); ASSERT_EQ(foo->fields.size(), 3U); ASSERT_TRUE(foo->HasField("x")); ASSERT_TRUE(foo->HasField("y")); ASSERT_TRUE(foo->HasField("a")); EXPECT_TRUE(foo->GetField("x").type.IsIntTy()); EXPECT_EQ(foo->GetField("x").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("x").offset, 0); EXPECT_TRUE(foo->GetField("y").type.IsIntTy()); EXPECT_EQ(foo->GetField("y").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("y").offset, 4); EXPECT_TRUE(foo->GetField("a").type.IsIntTy()); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 8); EXPECT_EQ(bar->size, 4); EXPECT_EQ(bar->fields.size(), 1U); EXPECT_TRUE(bar->HasField("z")); EXPECT_TRUE(bar->GetField("z").type.IsIntTy()); EXPECT_EQ(bar->GetField("z").type.GetSize(), 4U); EXPECT_EQ(bar->GetField("z").offset, 0); } TEST(clang_parser, nested_struct_anon_union_struct) { BPFtrace bpftrace; parse("struct Foo" "{" " union" " {" " long long _xy;" " struct { int x; int y;};" " };" " int a;" " struct { int z; };" "}", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 16); ASSERT_EQ(foo->fields.size(), 5U); ASSERT_TRUE(foo->HasField("_xy")); ASSERT_TRUE(foo->HasField("x")); ASSERT_TRUE(foo->HasField("y")); ASSERT_TRUE(foo->HasField("a")); ASSERT_TRUE(foo->HasField("z")); EXPECT_TRUE(foo->GetField("_xy").type.IsIntTy()); EXPECT_EQ(foo->GetField("_xy").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("_xy").offset, 0); EXPECT_TRUE(foo->GetField("x").type.IsIntTy()); EXPECT_EQ(foo->GetField("x").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("x").offset, 0); EXPECT_TRUE(foo->GetField("y").type.IsIntTy()); EXPECT_EQ(foo->GetField("y").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("y").offset, 4); EXPECT_TRUE(foo->GetField("a").type.IsIntTy()); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 8); EXPECT_TRUE(foo->GetField("z").type.IsIntTy()); EXPECT_EQ(foo->GetField("z").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("z").offset, 12); } TEST(clang_parser, bitfields) { BPFtrace bpftrace; parse("struct Foo { int a:8, b:8, c:16; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 4); ASSERT_EQ(foo->fields.size(), 3U); ASSERT_TRUE(foo->HasField("a")); ASSERT_TRUE(foo->HasField("b")); ASSERT_TRUE(foo->HasField("c")); // clang-tidy doesn't seem to acknowledge that ASSERT_*() will // return from function so that these are in fact checked accesses. // // NOLINTBEGIN(bugprone-unchecked-optional-access) EXPECT_TRUE(foo->GetField("a").type.IsIntTy()); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 0); ASSERT_TRUE(foo->GetField("a").bitfield.has_value()); EXPECT_EQ(foo->GetField("a").bitfield->read_bytes, 0x1U); EXPECT_EQ(foo->GetField("a").bitfield->access_rshift, 0U); EXPECT_EQ(foo->GetField("a").bitfield->mask, 0xFFU); EXPECT_TRUE(foo->GetField("b").type.IsIntTy()); EXPECT_EQ(foo->GetField("b").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("b").offset, 1); EXPECT_TRUE(foo->GetField("b").bitfield.has_value()); EXPECT_EQ(foo->GetField("b").bitfield->read_bytes, 0x1U); EXPECT_EQ(foo->GetField("b").bitfield->access_rshift, 0U); EXPECT_EQ(foo->GetField("b").bitfield->mask, 0xFFU); EXPECT_TRUE(foo->GetField("c").type.IsIntTy()); EXPECT_EQ(foo->GetField("c").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("c").offset, 2); EXPECT_TRUE(foo->GetField("c").bitfield.has_value()); EXPECT_EQ(foo->GetField("c").bitfield->read_bytes, 0x2U); EXPECT_EQ(foo->GetField("c").bitfield->access_rshift, 0U); EXPECT_EQ(foo->GetField("c").bitfield->mask, 0xFFFFU); // NOLINTEND(bugprone-unchecked-optional-access) } TEST(clang_parser, bitfields_uneven_fields) { BPFtrace bpftrace; parse("struct Foo { int a:1, b:1, c:3, d:20, e:7; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 4); ASSERT_EQ(foo->fields.size(), 5U); ASSERT_TRUE(foo->HasField("a")); ASSERT_TRUE(foo->HasField("b")); ASSERT_TRUE(foo->HasField("c")); ASSERT_TRUE(foo->HasField("d")); ASSERT_TRUE(foo->HasField("e")); // clang-tidy doesn't seem to acknowledge that ASSERT_*() will // return from function so that these are in fact checked accesses. // // NOLINTBEGIN(bugprone-unchecked-optional-access) EXPECT_TRUE(foo->GetField("a").type.IsIntTy()); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 0); ASSERT_TRUE(foo->GetField("a").bitfield.has_value()); EXPECT_EQ(foo->GetField("a").bitfield->read_bytes, 1U); EXPECT_EQ(foo->GetField("a").bitfield->access_rshift, 0U); EXPECT_EQ(foo->GetField("a").bitfield->mask, 0x1U); EXPECT_TRUE(foo->GetField("b").type.IsIntTy()); EXPECT_EQ(foo->GetField("b").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("b").offset, 0); ASSERT_TRUE(foo->GetField("b").bitfield.has_value()); EXPECT_EQ(foo->GetField("b").bitfield->read_bytes, 1U); EXPECT_EQ(foo->GetField("b").bitfield->access_rshift, 1U); EXPECT_EQ(foo->GetField("b").bitfield->mask, 0x1U); EXPECT_TRUE(foo->GetField("c").type.IsIntTy()); EXPECT_EQ(foo->GetField("c").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("c").offset, 0); ASSERT_TRUE(foo->GetField("c").bitfield.has_value()); EXPECT_EQ(foo->GetField("c").bitfield->read_bytes, 1U); EXPECT_EQ(foo->GetField("c").bitfield->access_rshift, 2U); EXPECT_EQ(foo->GetField("c").bitfield->mask, 0x7U); EXPECT_TRUE(foo->GetField("d").type.IsIntTy()); EXPECT_EQ(foo->GetField("d").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("d").offset, 0); ASSERT_TRUE(foo->GetField("d").bitfield.has_value()); EXPECT_EQ(foo->GetField("d").bitfield->read_bytes, 4U); EXPECT_EQ(foo->GetField("d").bitfield->access_rshift, 5U); EXPECT_EQ(foo->GetField("d").bitfield->mask, 0xFFFFFU); EXPECT_TRUE(foo->GetField("e").type.IsIntTy()); EXPECT_EQ(foo->GetField("e").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("e").offset, 3); ASSERT_TRUE(foo->GetField("e").bitfield.has_value()); EXPECT_EQ(foo->GetField("e").bitfield->read_bytes, 1U); EXPECT_EQ(foo->GetField("e").bitfield->access_rshift, 1U); EXPECT_EQ(foo->GetField("e").bitfield->mask, 0x7FU); // NOLINTEND(bugprone-unchecked-optional-access) } TEST(clang_parser, bitfields_with_padding) { BPFtrace bpftrace; parse("struct Foo { int pad; int a:28, b:4; long int end;}", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); // clang-tidy doesn't seem to acknowledge that ASSERT_*() will // return from function so that these are in fact checked accesses. // // NOLINTBEGIN(bugprone-unchecked-optional-access) EXPECT_EQ(foo->size, 16); ASSERT_EQ(foo->fields.size(), 4U); ASSERT_TRUE(foo->HasField("pad")); ASSERT_TRUE(foo->HasField("a")); ASSERT_TRUE(foo->HasField("b")); ASSERT_TRUE(foo->HasField("end")); EXPECT_TRUE(foo->GetField("a").type.IsIntTy()); EXPECT_EQ(foo->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("a").offset, 4); ASSERT_TRUE(foo->GetField("a").bitfield.has_value()); EXPECT_EQ(foo->GetField("a").bitfield->read_bytes, 4U); EXPECT_EQ(foo->GetField("a").bitfield->access_rshift, 0U); EXPECT_EQ(foo->GetField("a").bitfield->mask, 0xFFFFFFFU); EXPECT_TRUE(foo->GetField("b").type.IsIntTy()); EXPECT_EQ(foo->GetField("b").type.GetSize(), 4U); EXPECT_EQ(foo->GetField("b").offset, 7); ASSERT_TRUE(foo->GetField("b").bitfield.has_value()); EXPECT_EQ(foo->GetField("b").bitfield->read_bytes, 1U); EXPECT_EQ(foo->GetField("b").bitfield->access_rshift, 4U); EXPECT_EQ(foo->GetField("b").bitfield->mask, 0xFU); // NOLINTEND(bugprone-unchecked-optional-access) } TEST(clang_parser, builtin_headers) { // size_t is defined in stddef.h BPFtrace bpftrace; parse("#include \nstruct Foo { size_t x, y, z; }", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 24); ASSERT_EQ(foo->fields.size(), 3U); ASSERT_TRUE(foo->HasField("x")); ASSERT_TRUE(foo->HasField("y")); ASSERT_TRUE(foo->HasField("z")); EXPECT_TRUE(foo->GetField("x").type.IsIntTy()); EXPECT_EQ(foo->GetField("x").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("x").offset, 0); EXPECT_TRUE(foo->GetField("y").type.IsIntTy()); EXPECT_EQ(foo->GetField("y").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("y").offset, 8); EXPECT_TRUE(foo->GetField("z").type.IsIntTy()); EXPECT_EQ(foo->GetField("z").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("z").offset, 16); } TEST(clang_parser, macro_preprocessor) { BPFtrace bpftrace; parse("#define FOO size_t\n k:f { 0 }", bpftrace); parse("#define _UNDERSCORE 314\n k:f { 0 }", bpftrace); auto ¯os = bpftrace.macros_; ASSERT_EQ(macros.count("FOO"), 1U); EXPECT_EQ(macros["FOO"], "size_t"); ASSERT_EQ(macros.count("_UNDERSCORE"), 1U); EXPECT_EQ(macros["_UNDERSCORE"], "314"); } TEST(clang_parser, parse_fail) { BPFtrace bpftrace; parse("struct a { int a; struct b b; };", bpftrace, false); } class clang_parser_btf : public test_btf {}; TEST_F(clang_parser_btf, btf) { BPFtrace bpftrace; bpftrace.parse_btf({}); parse("struct Foo { " " struct Foo1 f1;" " struct Foo2 f2;" " struct Foo3 f3;" " struct task_struct t;" "}", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo1")); ASSERT_TRUE(bpftrace.structs.Has("struct Foo2")); ASSERT_TRUE(bpftrace.structs.Has("struct Foo3")); ASSERT_TRUE(bpftrace.structs.Has("struct task_struct")); auto foo1 = bpftrace.structs.Lookup("struct Foo1").lock(); auto foo2 = bpftrace.structs.Lookup("struct Foo2").lock(); auto foo3 = bpftrace.structs.Lookup("struct Foo3").lock(); auto task_struct = bpftrace.structs.Lookup("struct task_struct").lock(); EXPECT_EQ(foo1->size, 16); ASSERT_EQ(foo1->fields.size(), 3U); ASSERT_TRUE(foo1->HasField("a")); ASSERT_TRUE(foo1->HasField("b")); ASSERT_TRUE(foo1->HasField("c")); EXPECT_TRUE(foo1->GetField("a").type.IsIntTy()); EXPECT_EQ(foo1->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo1->GetField("a").offset, 0); EXPECT_TRUE(foo1->GetField("b").type.IsIntTy()); EXPECT_EQ(foo1->GetField("b").type.GetSize(), 1U); EXPECT_EQ(foo1->GetField("b").offset, 4); EXPECT_TRUE(foo1->GetField("c").type.IsIntTy()); EXPECT_EQ(foo1->GetField("c").type.GetSize(), 8U); EXPECT_EQ(foo1->GetField("c").offset, 8); EXPECT_EQ(foo2->size, 24); ASSERT_EQ(foo2->fields.size(), 3U); ASSERT_TRUE(foo2->HasField("a")); ASSERT_TRUE(foo2->HasField("f")); ASSERT_TRUE(foo2->HasField("g")); EXPECT_TRUE(foo2->GetField("a").type.IsIntTy()); EXPECT_EQ(foo2->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo2->GetField("a").offset, 0); EXPECT_TRUE(foo2->GetField("f").type.IsRecordTy()); EXPECT_EQ(foo2->GetField("f").type.GetSize(), 16U); EXPECT_EQ(foo2->GetField("f").offset, 8); EXPECT_TRUE(foo2->GetField("g").type.IsIntTy()); EXPECT_EQ(foo2->GetField("g").type.GetSize(), 1U); EXPECT_EQ(foo2->GetField("g").offset, 8); EXPECT_EQ(foo3->size, 16); ASSERT_EQ(foo3->fields.size(), 2U); ASSERT_TRUE(foo3->HasField("foo1")); ASSERT_TRUE(foo3->HasField("foo2")); EXPECT_EQ(task_struct->size, 16); ASSERT_EQ(task_struct->fields.size(), 7U); ASSERT_TRUE(task_struct->HasField("pid")); ASSERT_TRUE(task_struct->HasField("pgid")); ASSERT_TRUE(task_struct->HasField("a")); ASSERT_TRUE(task_struct->HasField("b")); ASSERT_TRUE(task_struct->HasField("c")); ASSERT_TRUE(task_struct->HasField("d")); auto foo1_field = foo3->GetField("foo1"); auto foo2_field = foo3->GetField("foo2"); EXPECT_TRUE(foo1_field.type.IsPtrTy()); EXPECT_EQ(foo1_field.type.GetPointeeTy()->GetName(), "struct Foo1"); EXPECT_EQ(foo1_field.offset, 0); EXPECT_TRUE(foo2_field.type.IsPtrTy()); EXPECT_EQ(foo2_field.type.GetPointeeTy()->GetName(), "struct Foo2"); EXPECT_EQ(foo2_field.offset, 8); } TEST_F(clang_parser_btf, btf_arrays_multi_dim) { GTEST_SKIP() << "BTF flattens multi-dimensional arrays #3082"; BPFtrace bpftrace; bpftrace.parse_btf({}); parse("struct Foo { struct Arrays a; };", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Arrays")); auto arrs = bpftrace.structs.Lookup("struct Arrays").lock(); ASSERT_TRUE(arrs->HasField("multi_dim")); EXPECT_TRUE(arrs->GetField("multi_dim").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("multi_dim").offset, 40); EXPECT_EQ(arrs->GetField("multi_dim").type.GetSize(), 24U); EXPECT_EQ(arrs->GetField("multi_dim").type.GetNumElements(), 3); EXPECT_TRUE(arrs->GetField("multi_dim").type.GetElementTy()->IsArrayTy()); EXPECT_EQ(arrs->GetField("multi_dim").type.GetElementTy()->GetSize(), 8U); EXPECT_EQ(arrs->GetField("multi_dim").type.GetElementTy()->GetNumElements(), 2); EXPECT_TRUE(arrs->GetField("multi_dim") .type.GetElementTy() ->GetElementTy() ->IsIntTy()); EXPECT_EQ(arrs->GetField("multi_dim") .type.GetElementTy() ->GetElementTy() ->GetSize(), 4U); } TEST(clang_parser, btf_unresolved_typedef) { // size_t is defined in stddef.h, but if we have BTF, it should be possible to // extract it from there BPFtrace bpftrace; bpftrace.parse_btf({}); if (!bpftrace.has_btf_data()) GTEST_SKIP(); parse("struct Foo { size_t x; };", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct Foo")); auto foo = bpftrace.structs.Lookup("struct Foo").lock(); EXPECT_EQ(foo->size, 8); ASSERT_EQ(foo->fields.size(), 1U); ASSERT_TRUE(foo->HasField("x")); EXPECT_TRUE(foo->GetField("x").type.IsIntTy()); EXPECT_EQ(foo->GetField("x").type.GetSize(), 8U); EXPECT_EQ(foo->GetField("x").offset, 0); } TEST_F(clang_parser_btf, btf_type_override) { // It should be possible to override types from BTF, ... BPFtrace bpftrace; bpftrace.parse_btf({}); parse("struct Foo1 { int a; };\n", bpftrace, true, "kprobe:sys_read { @x = ((struct Foo1 *)curtask); }"); ASSERT_TRUE(bpftrace.structs.Has("struct Foo1")); auto foo1 = bpftrace.structs.Lookup("struct Foo1").lock(); ASSERT_EQ(foo1->fields.size(), 1U); ASSERT_TRUE(foo1->HasField("a")); // ... however, in such case, no other types are taken from BTF and the // following will fail since Foo2 will be undefined bpftrace.btf_set_.clear(); parse("struct Foo1 { struct Foo2 foo2; };\n", bpftrace, false, "kprobe:sys_read { @x = ((struct Foo1 *)curtask); }"); // Here, Foo1 redefinition will take place when resolving incomplete types // (since Foo3 contains a pointer to Foo1) bpftrace.btf_set_.clear(); parse("struct Foo1 { struct Foo2 foo2; };\n", bpftrace, false, "kprobe:sys_read { @x1 = ((struct Foo3 *)curtask); }"); } TEST(clang_parser, struct_typedef) { // Make sure we can differentiate between "struct max_align_t {}" and // "typedef struct {} max_align_t" BPFtrace bpftrace; parse("#include <__stddef_max_align_t.h>\n" "struct max_align_t { int x; };", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct max_align_t")); ASSERT_TRUE(bpftrace.structs.Has("max_align_t")); auto max_align_struct = bpftrace.structs.Lookup("struct max_align_t").lock(); auto max_align_typedef = bpftrace.structs.Lookup("max_align_t").lock(); // Non-typedef'd struct EXPECT_EQ(max_align_struct->size, 4); ASSERT_EQ(max_align_struct->fields.size(), 1U); ASSERT_TRUE(max_align_struct->HasField("x")); EXPECT_TRUE(max_align_struct->GetField("x").type.IsIntTy()); EXPECT_EQ(max_align_struct->GetField("x").type.GetSize(), 4U); EXPECT_EQ(max_align_struct->GetField("x").offset, 0); // typedef'd struct (defined in __stddef_max_align_t.h builtin header) EXPECT_EQ(max_align_typedef->size, 32); ASSERT_EQ(max_align_typedef->fields.size(), 2U); ASSERT_TRUE(max_align_typedef->HasField("__clang_max_align_nonce1")); ASSERT_TRUE(max_align_typedef->HasField("__clang_max_align_nonce2")); EXPECT_TRUE( max_align_typedef->GetField("__clang_max_align_nonce1").type.IsIntTy()); EXPECT_EQ( max_align_typedef->GetField("__clang_max_align_nonce1").type.GetSize(), 8U); EXPECT_EQ(max_align_typedef->GetField("__clang_max_align_nonce1").offset, 0); // double are not parsed correctly yet so these fields are junk for now EXPECT_TRUE( max_align_typedef->GetField("__clang_max_align_nonce2").type.IsNoneTy()); EXPECT_EQ( max_align_typedef->GetField("__clang_max_align_nonce2").type.GetSize(), 0U); EXPECT_EQ(max_align_typedef->GetField("__clang_max_align_nonce2").offset, 16); } TEST(clang_parser, struct_qualifiers) { BPFtrace bpftrace; parse("struct a {int a} struct b { volatile const struct a* restrict a; " "const struct a a2; };", bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct b")); auto SB = bpftrace.structs.Lookup("struct b").lock(); EXPECT_EQ(SB->size, 16); EXPECT_EQ(SB->fields.size(), 2U); EXPECT_TRUE(SB->GetField("a").type.IsPtrTy()); EXPECT_TRUE(SB->GetField("a").type.GetPointeeTy()->IsRecordTy()); EXPECT_EQ(SB->GetField("a").type.GetPointeeTy()->GetName(), "struct a"); EXPECT_TRUE(SB->GetField("a2").type.IsRecordTy()); EXPECT_EQ(SB->GetField("a2").type.GetName(), "struct a"); } TEST(clang_parser, redefined_types) { BPFtrace bpftrace; parse("struct a {int a;}; struct a {int a;};", bpftrace, false); parse("struct a {int a;}; struct a {int a; short b;};", bpftrace, false); } TEST(clang_parser, data_loc_annotation) { BPFtrace bpftrace; std::string input = R"_( struct _tracepoint_irq_irq_handler_entry { int common_pid; int irq; __attribute__((annotate("tp_data_loc"))) char * name; }; )_"; parse(input, bpftrace); ASSERT_TRUE(bpftrace.structs.Has("struct _tracepoint_irq_irq_handler_entry")); auto s = bpftrace.structs.Lookup("struct _tracepoint_irq_irq_handler_entry") .lock(); EXPECT_EQ(s->size, 16); EXPECT_EQ(s->fields.size(), 3U); EXPECT_TRUE(s->GetField("common_pid").type.IsIntTy()); EXPECT_TRUE(s->GetField("irq").type.IsIntTy()); // The parser needs to rewrite __data_loc fields to be u64 so it can hold // a pointer to the actual data. The kernel tracepoint infra exports an // encoded u32 which codegen will know how to decode. EXPECT_TRUE(s->GetField("name").is_data_loc); ASSERT_TRUE(s->GetField("name").type.IsIntTy()); EXPECT_EQ(s->GetField("name").type.GetIntBitWidth(), 64ULL); } } // namespace bpftrace::test::clang_parser bpftrace-0.23.2/tests/codegen-tests.sh000077500000000000000000000020301477746507000176450ustar00rootroot00000000000000#!/bin/bash # Runs or updates codegen tests' expected LLVM IR. # Requires nix. # # Example usage: # # ./tests/codegen-tests.sh # set -eu BUILD_DIR=build-codegen-update UPDATE_TESTS=${BPFTRACE_UPDATE_TESTS:-0} SCRIPT_NAME=$0 function run() { nix develop .#bpftrace-llvm18 --command "$@" } usage() { echo "Usage:" echo " ${SCRIPT_NAME} [OPTIONS]" echo "" echo " Run or update the codegen tests with nix." echo "" echo "OPTIONS" echo " -u Update the tests." echo " -d Change the default build directory. Default: ${BUILD_DIR}" } while getopts ":d:uh" opt; do case ${opt} in u ) UPDATE_TESTS=1 ;; d ) BUILD_DIR=${OPTARG} ;; h ) usage exit 0 ;; esac done # Change dir to project root cd "$(git rev-parse --show-toplevel)" run cmake -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Debug run make -C "$BUILD_DIR" -j $(nproc) bpftrace_test BPFTRACE_UPDATE_TESTS=${UPDATE_TESTS} run ./"$BUILD_DIR"/tests/bpftrace_test --gtest_filter="codegen*" bpftrace-0.23.2/tests/codegen-validator.sh000077500000000000000000000011001477746507000204650ustar00rootroot00000000000000#!/bin/bash ## Don't add to this EXIT=0 LLVM_VERSION=18 if ! LLVM=$(command -v "llvm-as-${LLVM_VERSION}" || command -v llvm-as); then echo "llvm-as not found, exiting" exit 1 fi if ! $LLVM --version | grep -q "LLVM version ${LLVM_VERSION}"; then echo "llvm-as is not version ${LLVM_VERSION}" fi if [[ -z "$1" ]]; then echo "Usage: $0 " exit 1 fi for file in "${1}"/tests/codegen/llvm/*.ll; do if $LLVM -o /dev/null "${file}"; then echo -e "[ OK ]\t$file" else echo -e "[ FAILED ]\t$file" EXIT=1 fi done exit $EXIT bpftrace-0.23.2/tests/codegen/000077500000000000000000000000001477746507000161535ustar00rootroot00000000000000bpftrace-0.23.2/tests/codegen/argN_rawtracepoint.cpp000066400000000000000000000003771477746507000225170ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, argN_rawtracepoint) { test("rawtracepoint:sched_switch { @[arg0] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/args_multiple_tracepoints.cpp000066400000000000000000000005731477746507000241460ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" using ::testing::_; using ::testing::Return; namespace bpftrace { namespace test { namespace codegen { TEST(codegen, args_multiple_tracepoints) { test("tracepoint:sched:sched_one,tracepoint:sched:sched_two { " "@[args.common_field] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/args_multiple_tracepoints_category_wild.cpp000066400000000000000000000005341477746507000270570ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" using ::testing::_; using ::testing::Return; namespace bpftrace { namespace test { namespace codegen { TEST(codegen, args_multiple_tracepoints_category_wild) { test("tracepoint:sched*:sched_* { @[args.common_field] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/args_multiple_tracepoints_wild.cpp000066400000000000000000000005221477746507000251570ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" using ::testing::_; using ::testing::Return; namespace bpftrace { namespace test { namespace codegen { TEST(codegen, args_multiple_tracepoints_wild) { test("tracepoint:sched:sched_* { @[args.common_field] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/array_integer_equal_comparison.cpp000066400000000000000000000012661477746507000251400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { namespace array_integer_equal_comparison { constexpr auto PROG = "struct Foo { int arr[4]; }" "kprobe:f" "{" " $a = ((struct Foo *)arg0)->arr;" " $b = ((struct Foo *)arg0)->arr;" " if ($a == $b)" " {" " exit();" " }" "}"; TEST(codegen, array_integer_equal_comparison) { test(PROG, NAME); } } // namespace array_integer_equal_comparison } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/basic_while_loop.cpp000066400000000000000000000003701477746507000221610ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, basic_while_loop) { test("i:s:1 { $a = 1; while ($a <= 150) { @=$a++; }}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/binop_int_promotion.cpp000066400000000000000000000003741477746507000227520ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, binop_int_promotion) { test("kretprobe:f { $x = (uint32)5; $x += (uint16)1 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/bitshift_left.cpp000066400000000000000000000003511477746507000215040ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, bitshift_left) { test("kprobe:f { @x = 1 << 10; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/bitshift_right.cpp000066400000000000000000000003541477746507000216720ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, bitshift_right) { test("kprobe:f { @x = 1024 >> 9; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/bitwise_not.cpp000066400000000000000000000003301477746507000212010ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, bitwise_not) { test("BEGIN { @x = ~10; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_arg.cpp000066400000000000000000000003561477746507000211620ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_arg) { test("kprobe:f { @x = arg0; @y = arg2 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_comm.cpp000066400000000000000000000003341477746507000213400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_comm) { test("kprobe:f { @x = comm }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_cpid.cpp000066400000000000000000000005551477746507000213310ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_cpid) { auto bpftrace = get_mock_bpftrace(); bpftrace->child_ = std::make_unique(""); bpftrace->helper_check_level_ = 0; test(*bpftrace, "kprobe:f { @ = cpid }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_cpu.cpp000066400000000000000000000003421477746507000211730ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_cpu) { test("kprobe:f { @x = cpu }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_ctx.cpp000066400000000000000000000003421477746507000212020ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_ctx) { test("kprobe:f { @x = (uint64)ctx }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_ctx_field.cpp000066400000000000000000000007141477746507000223500ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_ctx_field) { std::string prog = R"END( struct c { char c; }; struct x { long a; short b[4]; struct c c; struct c *d; char e[4] }; kprobe:f { $x = (struct x*)ctx; @a = $x->a; @b = $x->b[0]; @c = $x->c.c; @d = $x->d->c; @e = $x->e; } )END"; test(prog, NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_curtask.cpp000066400000000000000000000003521477746507000220610ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_curtask) { test("kprobe:f { @x = curtask }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_elapsed.cpp000066400000000000000000000003471477746507000220260ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_elapsed) { test("i:s:1 { @ = elapsed; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_func.cpp000066400000000000000000000010771477746507000213450ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_func_kprobe) { test("kprobe:f { @x = func }", NAME); } TEST(codegen, builtin_func_kretprobe) { test("kretprobe:f { @x = func }", NAME); } TEST(codegen, builtin_func_uprobe) { test("uprobe:/bin/sh:f { @x = func }", NAME); } TEST(codegen, builtin_func_uretprobe) { test("uretprobe:/bin/sh:f { @x = func }", NAME); } TEST(codegen, builtin_func_fentry) { test("fentry:f { @x = func }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_func_wild.cpp000066400000000000000000000004251477746507000223600ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { using ::testing::Return; TEST(codegen, builtin_func_wild) { test("kprobe:sys_* { @x = func }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_jiffies.cpp000066400000000000000000000003521477746507000220240ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_jiffies) { test("kprobe:f { @x = jiffies }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_kstack.cpp000066400000000000000000000003501477746507000216630ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_kstack) { test("kprobe:f { @x = kstack }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_nsecs.cpp000066400000000000000000000003461477746507000215230ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_nsecs) { test("kprobe:f { @x = nsecs }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_numaid.cpp000066400000000000000000000003501477746507000216600ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_numaid) { test("kprobe:f { @x = numaid }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_pid_tid.cpp000066400000000000000000000007101477746507000220170ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_pid_tid) { test("kprobe:f { @x = pid; @y = tid }", NAME); } TEST(codegen, builtin_pid_tid_namespace) { auto bpftrace = get_mock_bpftrace(); bpftrace->mock_in_init_pid_ns = false; bpftrace->helper_check_level_ = 0; test(*bpftrace, "kprobe:f { @x = pid; @y = tid }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_probe.cpp000066400000000000000000000006411477746507000215150ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_probe) { test("tracepoint:sched:sched_one { @x = probe }", NAME); } TEST(codegen, builtin_probe_comparison) { test( R"(tracepoint:sched:sched_one { if (probe == "tracepoint:sched:sched_one") {} })", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_probe_wild.cpp000066400000000000000000000004451477746507000225360ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { using ::testing::Return; TEST(codegen, builtin_probe_wild) { test("tracepoint:sched:sched_on* { @x = probe }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_rand.cpp000066400000000000000000000003441477746507000213320ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_rand) { test("kprobe:f { @x = rand }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_retval.cpp000066400000000000000000000003531477746507000217030ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_retval) { test("kretprobe:f { @x = retval }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_sarg.cpp000066400000000000000000000003611477746507000213410ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_sarg) { test("kprobe:f { @x = sarg0; @y = sarg2 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_uid_gid.cpp000066400000000000000000000003601477746507000220100ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_uid_gid) { test("kprobe:f { @x = uid; @y = gid }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_username.cpp000066400000000000000000000003651477746507000222300ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_username) { test("kprobe:f { @x = username; @y = gid}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/builtin_ustack.cpp000066400000000000000000000003501477746507000216750ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, builtin_ustack) { test("kprobe:f { @x = ustack }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_avg.cpp000066400000000000000000000003341477746507000204270ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_avg) { test("kprobe:f { @x = avg(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_buf.cpp000066400000000000000000000007671477746507000204400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_buf_implicit_size) { test("struct x { int c[4] }; kprobe:f { $foo = (struct x*)0; @x = " "buf($foo->c); }", NAME); } TEST(codegen, call_buf_size_literal) { test("kprobe:f { @x = buf(arg0, 1) }", NAME); } TEST(codegen, call_buf_size_nonliteral) { test("kprobe:f { @x = buf(arg0, arg1) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_cat.cpp000066400000000000000000000003561477746507000204250ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_cat) { test("kprobe:f { cat(\"/proc/loadavg\"); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_cgroup.cpp000066400000000000000000000004431477746507000211520ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_cgroup) { test("tracepoint:syscalls:sys_enter_openat /cgroup == 0x100000001/ { @x = " "cgroup }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_cgroup_path.cpp000066400000000000000000000003621477746507000221660ustar00rootroot00000000000000#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 bpftrace-0.23.2/tests/codegen/call_clear.cpp000066400000000000000000000003651477746507000207440ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_clear) { test("BEGIN { @x = 1; } kprobe:f { clear(@x); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_count.cpp000066400000000000000000000003451477746507000210040ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_count) { test("kprobe:f { @x = count() }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_delete.cpp000066400000000000000000000005341477746507000211160ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_delete) { test("kprobe:f { @x[1] = 1; delete(@x, 1) }", NAME); } TEST(codegen, call_delete_deprecated) { test("kprobe:f { @x[1] = 1; delete(@x[1]) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_exit.cpp000066400000000000000000000004611477746507000206240ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_exit) { test("kprobe:f { exit(); @=10 }", NAME); } TEST(codegen, call_exit_with_error_code) { test("kprobe:f { exit(1); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_has_key.cpp000066400000000000000000000003541477746507000212770ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_has_key) { test("kprobe:f { @x[1] = 1; has_key(@x, 1) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_hist.cpp000066400000000000000000000003461477746507000206240ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_hist) { test("kprobe:f { @x = hist(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_join.cpp000066400000000000000000000010531477746507000206100ustar00rootroot00000000000000#include "common.h" #include "config.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_join) { test("struct arg { char **argv } kprobe:f { $x = (struct arg *) 0; " "join($x->argv); }", NAME); } TEST(codegen, call_join_with_debug) { auto bpftrace = get_mock_bpftrace(); bpftrace->debug_output_ = true; test(*bpftrace, "struct arg { char **argv } kprobe:f { $x = (struct arg *) 0; " "join($x->argv); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_kstack.cpp000066400000000000000000000046621477746507000211420ustar00rootroot00000000000000#include "common.h" #include namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_kstack) { auto result = NAME; test("kprobe:f { @x = kstack(); @y = kstack(6); @z = kstack(perf) }", result); } TEST(codegen, call_kstack_mapids) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str( "kprobe:f { @x = kstack(5); @y = kstack(6); @z = kstack(6) }"), 0); ClangParser clang; clang.parse(driver.ctx.root, *bpftrace); ast::SemanticAnalyser semantics(driver.ctx, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.ctx, *bpftrace); auto resources_optional = resource_analyser.analyse(); ASSERT_TRUE(resources_optional.has_value()); bpftrace->resources = resources_optional.value(); ast::CodegenLLVM codegen(driver.ctx, *bpftrace); bpftrace->bytecode_ = codegen.compile(); ASSERT_EQ(bpftrace->bytecode_.maps().size(), 8); ASSERT_EQ(bpftrace->bytecode_.countStackMaps(), 3U); StackType stack_type; stack_type.limit = 5; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); stack_type.limit = 6; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); } TEST(codegen, call_kstack_modes_mapids) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str( "kprobe:f { @w = kstack(raw); @x = kstack(perf); @y = " "kstack(bpftrace); @z = kstack() }"), 0); ClangParser clang; clang.parse(driver.ctx.root, *bpftrace); ast::SemanticAnalyser semantics(driver.ctx, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.ctx, *bpftrace); auto resources_optional = resource_analyser.analyse(); ASSERT_TRUE(resources_optional.has_value()); bpftrace->resources = resources_optional.value(); ast::CodegenLLVM codegen(driver.ctx, *bpftrace); bpftrace->bytecode_ = codegen.compile(); ASSERT_EQ(bpftrace->bytecode_.maps().size(), 10); ASSERT_EQ(bpftrace->bytecode_.countStackMaps(), 4U); StackType stack_type; stack_type.mode = StackMode::perf; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); stack_type.mode = StackMode::bpftrace; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); stack_type.mode = StackMode::raw; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_len.cpp000066400000000000000000000013501477746507000204270ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { namespace call_len { constexpr auto PROG = "BEGIN { @x[1] = 1; } kprobe:f { $s = len(@x); }"; TEST_F(codegen_btf, call_len_for_each_map_elem) { auto bpftrace = get_mock_bpftrace(); auto feature = std::make_unique(); feature->mock_missing_kernel_func(Kfunc::bpf_map_sum_elem_count); bpftrace->feature_ = std::move(feature); test(*bpftrace, PROG, NAME); } TEST_F(codegen_btf, call_len_map_sum_elem_count) { test(PROG, NAME); } TEST_F(codegen_btf, call_len_ustack_kstack) { test("kprobe:f { @x = len(ustack); @y = len(kstack); }", NAME); } } // namespace call_len } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_lhist.cpp000066400000000000000000000003631477746507000207770ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_lhist) { test("kprobe:f { @x = lhist(pid, 0, 100, 1) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_macaddr.cpp000066400000000000000000000004571477746507000212530ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_macaddr) { test("struct mac { unsigned char addr[6] } kprobe:f { @x = macaddr(((struct " "mac*)0)->addr); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_max.cpp000066400000000000000000000003441477746507000204400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_max) { test("kprobe:f { @x = max(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_min.cpp000066400000000000000000000003441477746507000204360ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_min) { test("kprobe:f { @x = min(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_nsecs_boot.cpp000066400000000000000000000003421477746507000220070ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_nsecs_boot) { test("k:f { @x = nsecs(boot); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_nsecs_monotonic.cpp000066400000000000000000000003541477746507000230540ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_nsecs_monotonic) { test("k:f { @x = nsecs(monotonic); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_nsecs_tai.cpp000066400000000000000000000003401477746507000216170ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_nsecs_tai) { test("k:f { @x = nsecs(tai); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_ntop_char16.cpp000066400000000000000000000004631477746507000220010ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_ntop_char16) { test("struct inet { unsigned char addr[16] } kprobe:f { @x = ntop(((struct " "inet*)0)->addr); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_ntop_char4.cpp000066400000000000000000000004611477746507000217140ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_ntop_char4) { test("struct inet { unsigned char addr[4] } kprobe:f { @x = ntop(((struct " "inet*)0)->addr); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_ntop_key.cpp000066400000000000000000000003701477746507000215020ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_ntop_key) { test("kprobe:f { @x[ntop(2, 0xFFFFFFFF)] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_offsetof.cpp000066400000000000000000000007231477746507000214670ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_offsetof) { test("struct Foo { int x; long l; char c; }" "BEGIN { @x = offsetof(struct Foo, x); exit(); }", NAME); } TEST(codegen, call_offsetof_sub_field) { test("struct Foo { struct Bar { int a; } d; }" "BEGIN { @x = offsetof(struct Foo, d.a); exit(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_override.cpp000066400000000000000000000003521477746507000214710ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_override) { test("kprobe:f { override(arg0); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_override_literal.cpp000066400000000000000000000003601477746507000232040ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_override_literal) { test("kprobe:f { override(-1); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_path.cpp000066400000000000000000000003521477746507000206060ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_path) { test("fentry:filp_close { path((uint8 *)0); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_path_with_optional_size.cpp000066400000000000000000000004011477746507000245730ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_path_with_optional_size) { test("fentry:filp_close { path((uint8 *)0, 48); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_percpu_kaddr.cpp000066400000000000000000000005451477746507000223210ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_percpu_kaddr) { test("BEGIN { percpu_kaddr(\"process_counts\", 0); }", NAME); } TEST(codegen, call_percpu_kaddr_this_cpu) { test("BEGIN { percpu_kaddr(\"process_counts\"); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_print.cpp000066400000000000000000000003651477746507000210120ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_print) { test("BEGIN { @x = 1; } kprobe:f { print(@x); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_print_non_map.cpp000066400000000000000000000004731477746507000225210ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_print_int) { test("k:f { print(3) }", NAME); } TEST(codegen, call_print_composit) { test("k:f { print((1,\"abc\")) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_printf.cpp000066400000000000000000000005041477746507000211530ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_printf) { test("struct Foo { char c; long l; } kprobe:f { $foo = (struct Foo*)arg0; " "printf(\"%c %lu\\n\", $foo->c, $foo->l) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_reg.cpp000066400000000000000000000004671477746507000204360ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { #ifdef __x86_64__ TEST(codegen, call_reg) // Identical to builtin_func apart from variable names { test("kprobe:f { @x = reg(\"ip\") }", NAME); } #endif } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_signal.cpp000066400000000000000000000003411477746507000211250ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_signal) { test("k:f { signal(arg0); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_signal_literal.cpp000066400000000000000000000003461477746507000226460ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_signal_literal) { test("k:f { signal(8); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_signal_string_literal.cpp000066400000000000000000000003671477746507000242370ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_signal_string_literal) { test("k:f { signal(\"SIGKILL\"); }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_sizeof.cpp000066400000000000000000000004141477746507000211500ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_sizeof) { test("struct Foo { int x; char c; } BEGIN { @x = sizeof(struct Foo) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_stats.cpp000066400000000000000000000003501477746507000210060ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_stats) { test("kprobe:f { @x = stats(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_str.cpp000066400000000000000000000003451477746507000204640ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_str) { test("kprobe:f { @x = str(arg0) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_str_2_expr.cpp000066400000000000000000000003621477746507000217420ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_str_2_expr) { test("kprobe:f { @x = str(arg0, arg1) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_str_2_lit.cpp000066400000000000000000000003561477746507000215570ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_str_2_lit) { test("kprobe:f { @x = str(arg0, 6) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_strftime.cpp000066400000000000000000000003571477746507000215140ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_strftime) { test("kprobe:f { strftime(\"%M:%S\", nsecs); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_sum.cpp000066400000000000000000000003441477746507000204570ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_sum) { test("kprobe:f { @x = sum(pid) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_system.cpp000066400000000000000000000004011477746507000211710ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_system) { test(" kprobe:f { system(\"echo %d\", 100) }", NAME, false); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_time.cpp000066400000000000000000000003371477746507000206130ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_time) { test("kprobe:f { time(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_uptr.cpp000066400000000000000000000004751477746507000206520ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_uptr) { test("k:f { @=*uptr((int16*) arg0 ); }", std::string(NAME) + "_1"); test("k:f { @=*uptr((int32*) arg0 ); }", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_ustack.cpp000066400000000000000000000046621477746507000211540ustar00rootroot00000000000000#include "common.h" #include namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_ustack) { auto result = NAME; test("kprobe:f { @x = ustack(); @y = ustack(6); @z = ustack(perf) }", result); } TEST(codegen, call_ustack_mapids) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str( "kprobe:f { @x = ustack(5); @y = ustack(6); @z = ustack(6) }"), 0); ClangParser clang; clang.parse(driver.ctx.root, *bpftrace); ast::SemanticAnalyser semantics(driver.ctx, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.ctx, *bpftrace); auto resources_optional = resource_analyser.analyse(); ASSERT_TRUE(resources_optional.has_value()); bpftrace->resources = resources_optional.value(); ast::CodegenLLVM codegen(driver.ctx, *bpftrace); bpftrace->bytecode_ = codegen.compile(); ASSERT_EQ(bpftrace->bytecode_.maps().size(), 8); ASSERT_EQ(bpftrace->bytecode_.countStackMaps(), 3U); StackType stack_type; stack_type.limit = 5; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); stack_type.limit = 6; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); } TEST(codegen, call_ustack_modes_mapids) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str( "kprobe:f { @w = ustack(raw); @x = ustack(perf); @y = " "ustack(bpftrace); @z = ustack() }"), 0); ClangParser clang; clang.parse(driver.ctx.root, *bpftrace); ast::SemanticAnalyser semantics(driver.ctx, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.ctx, *bpftrace); auto resources_optional = resource_analyser.analyse(); ASSERT_TRUE(resources_optional.has_value()); bpftrace->resources = resources_optional.value(); ast::CodegenLLVM codegen(driver.ctx, *bpftrace); bpftrace->bytecode_ = codegen.compile(); ASSERT_EQ(bpftrace->bytecode_.maps().size(), 10); ASSERT_EQ(bpftrace->bytecode_.countStackMaps(), 4U); StackType stack_type; stack_type.mode = StackMode::perf; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); stack_type.mode = StackMode::bpftrace; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); stack_type.mode = StackMode::raw; ASSERT_TRUE(bpftrace->bytecode_.hasMap(stack_type)); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_usym_key.cpp000066400000000000000000000003541477746507000215210ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_usym_key) { test("kprobe:f { @x[usym(0)] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/call_zero.cpp000066400000000000000000000003631477746507000206330ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, call_zero) { test("BEGIN { @x = 1; } kprobe:f { zero(@x); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/cast_arr_to_int.cpp000066400000000000000000000003641477746507000220340ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, cast_arr_to_int) { test("kprobe:f { @=(uint32)pton(\"127.0.0.1\"); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/cast_int_to_arr.cpp.cpp000066400000000000000000000003601477746507000226110ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, cast_int_to_arr) { test("kprobe:f { $a=(uint8[8])0; @ = $a[0]; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/common.h000066400000000000000000000057151477746507000176240ustar00rootroot00000000000000#pragma once #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "../mocks.h" #include "ast/passes/codegen_llvm.h" #include "ast/passes/field_analyser.h" #include "ast/passes/resource_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpffeature.h" #include "bpftrace.h" #include "clang_parser.h" #include "driver.h" #include "tracepoint_format_parser.h" #include "btf_common.h" namespace bpftrace { namespace test { namespace codegen { #define NAME (::testing::UnitTest::GetInstance()->current_test_info()->name()) class codegen_btf : public test_btf {}; static std::string get_expected(const std::string &name) { std::string fname = TEST_CODEGEN_LOCATION + name + ".ll"; std::ifstream file; file.open(fname); if (file.good()) return std::string((std::istreambuf_iterator(file)), (std::istreambuf_iterator())); throw std::runtime_error("Could not find codegen result for test: " + name); } // This is the lower level codegen test entrypoint. // // The contract is that the `bpftrace` must be completely initialized and ready // to go (eg. members replaced with mocks as necessary) before calling into // here. static void test(BPFtrace &bpftrace, const std::string &input, const std::string &name) { Driver driver(bpftrace); ASSERT_EQ(driver.parse_str(input), 0); ast::FieldAnalyser fields(driver.ctx, bpftrace); ASSERT_EQ(fields.analyse(), 0); ClangParser clang; clang.parse(driver.ctx.root, bpftrace); ASSERT_EQ(driver.parse_str(input), 0); ast::SemanticAnalyser semantics(driver.ctx, bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.ctx, bpftrace); auto resources_optional = resource_analyser.analyse(); ASSERT_TRUE(resources_optional.has_value()); bpftrace.resources = resources_optional.value(); std::stringstream out; ast::CodegenLLVM codegen(driver.ctx, bpftrace); codegen.generate_ir(); codegen.DumpIR(out); // Test that generated code compiles cleanly codegen.optimize(); codegen.emit(false); uint64_t update_tests = 0; get_uint64_env_var("BPFTRACE_UPDATE_TESTS", [&](uint64_t x) { update_tests = x; }); if (update_tests >= 1) { std::cerr << "Running in update mode, test is skipped" << std::endl; std::ofstream file(TEST_CODEGEN_LOCATION + name + ".ll"); file << out.str(); return; } std::string expected_output = get_expected(name); EXPECT_EQ(expected_output, out.str()) << "the following program failed: '" << input << "'"; } // This is the common case codegen test entrypoint. // // Please prefer to use this interface. static void test(const std::string &input, const std::string &name, bool safe_mode = true) { auto bpftrace = get_mock_bpftrace(); bpftrace->safe_mode_ = safe_mode; test(*bpftrace, input, name); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/comparison_extend.cpp000066400000000000000000000004171477746507000224020ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, comparison_extend) { // Make sure i1 is zero extended test("kprobe:f { @ = 1 < arg0 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/dereference.cpp000066400000000000000000000003521477746507000211260ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, dereference) { test("kprobe:f { @x = *kptr(1234) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/empty_function.cpp000066400000000000000000000003371477746507000217250ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, empty_function) { test("kprobe:f { 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/enum.cpp000066400000000000000000000003731477746507000176260ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, enum_declaration) { test("enum { a = 42, b } k:f { @a = a; @b = b }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/fentry_dereference.cpp000066400000000000000000000004121477746507000225120ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, fentry_dereference) { test("fentry:tcp_sendmsg { @[args->sk->__sk_common.skc_daddr] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/fentry_recursion_check.cpp000066400000000000000000000011571477746507000234200ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, fentry_recursion_check) { auto bpftrace = get_mock_bpftrace(); bpftrace->need_recursion_check_ = true; test(*bpftrace, "fentry:queued_spin_lock_slowpath { }" "tracepoint:exceptions:page_fault_user { }", NAME); } TEST(codegen, fentry_recursion_check_with_predicate) { auto bpftrace = get_mock_bpftrace(); bpftrace->need_recursion_check_ = true; test(*bpftrace, "fentry:queued_spin_lock_slowpath / pid == 1234 / { }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/fexit_dereference.cpp000066400000000000000000000004031477746507000223220ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, fexit_dereference) { test("fexit:sk_alloc { @[retval->__sk_common.skc_daddr] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/for_map_one_key.cpp000066400000000000000000000003161477746507000220130ustar00rootroot00000000000000#include "common.h" namespace bpftrace::test::codegen { TEST(codegen, for_map_one_key) { test("BEGIN { @map[16] = 32; for ($kv : @map) { @x = $kv; } }", NAME); } } // namespace bpftrace::test::codegen bpftrace-0.23.2/tests/codegen/for_map_strings.cpp000066400000000000000000000003361477746507000220550ustar00rootroot00000000000000#include "common.h" namespace bpftrace::test::codegen { TEST(codegen, for_map_strings) { test(R"(BEGIN { @map["abc"] = "xyz"; for ($kv : @map) { @x = $kv; } })", NAME); } } // namespace bpftrace::test::codegen bpftrace-0.23.2/tests/codegen/for_map_two_keys.cpp000066400000000000000000000003221477746507000222230ustar00rootroot00000000000000#include "common.h" namespace bpftrace::test::codegen { TEST(codegen, for_map_two_keys) { test("BEGIN { @map[16,17] = 32; for ($kv : @map) { @x = $kv; } }", NAME); } } // namespace bpftrace::test::codegen bpftrace-0.23.2/tests/codegen/for_map_variables.cpp000066400000000000000000000012541477746507000223340ustar00rootroot00000000000000#include "common.h" namespace bpftrace::test::codegen { TEST(codegen, for_map_variables) { test(R"( BEGIN { @map[16] = 32; $var1 = 123; $var2 = "abc"; $var3 = "def"; for ($kv : @map) { $var1++; $can_read = $var3; } @len = $var1; })", NAME); } TEST(codegen, for_map_variables_multiple_loops) { test(R"( BEGIN { @[0] = 0; $var1 = 0; $var2 = 0; // Ensure we get unique ctx_t types for each loop for ($_ : @) { $var1++; } for ($_ : @) { $var1++; $var2++; } })", NAME); } } // namespace bpftrace::test::codegen bpftrace-0.23.2/tests/codegen/for_map_variables_scope.cpp000066400000000000000000000011101477746507000235140ustar00rootroot00000000000000#include "common.h" namespace bpftrace::test::codegen { TEST(codegen, for_map_variables_scope) { // This test is to ensure that if variables are defined with the same name // in different loops, then they are each given their own stack allocations // and are not mixed up. Once we have proper variable scoping (#3017), the // special casing for for-loop codegen can go and this test can be removed. test("BEGIN { @map[16] = 32;\n" "for ($kv : @map) { $var = 1; }\n" "for ($kv : @map) { $var = 1; } }", NAME); } } // namespace bpftrace::test::codegen bpftrace-0.23.2/tests/codegen/general.cpp000066400000000000000000000062161477746507000203010ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include "ast/ast.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { using ::testing::_; class MockBPFtrace : public BPFtrace { public: #pragma GCC diagnostic push #ifdef __clang__ #pragma GCC diagnostic ignored "-Winconsistent-missing-override" #endif MOCK_METHOD4(add_probe, int(ast::ASTContext &, const ast::AttachPoint &, const ast::Probe &, int)); #pragma GCC diagnostic pop int resolve_uname(const std::string &name, struct symbol *sym, const std::string &path) const override { (void)path; sym->name = name; sym->address = 12345; sym->size = 4; return 0; } bool is_traceable_func( const std::string &__attribute__((unused))) const override { return true; } bool has_kprobe_multi(void) { return feature_->has_kprobe_multi(); } }; TEST(codegen, printf_offsets) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str( "struct Foo { char c; int i; char str[10]; }\n" "kprobe:f\n" "{\n" " $foo = (struct Foo*)arg0;\n" " printf(\"%c %u %s %p\\n\", $foo->c, $foo->i, $foo->str, 0)\n" "}"), 0); ClangParser clang; clang.parse(driver.ctx.root, *bpftrace); ast::SemanticAnalyser semantics(driver.ctx, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.ctx, *bpftrace); auto resources_optional = resource_analyser.analyse(); ASSERT_TRUE(resources_optional.has_value()); bpftrace->resources = resources_optional.value(); ast::CodegenLLVM codegen(driver.ctx, *bpftrace); codegen.generate_ir(); EXPECT_EQ(bpftrace->resources.printf_args.size(), 1U); 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"); EXPECT_EQ(args.size(), 4U); // Note that scalar types are promoted to 64-bits when put into // a perf event buffer EXPECT_TRUE(args[0].type.IsIntTy()); EXPECT_EQ(args[0].type.GetSize(), 8U); EXPECT_EQ(args[0].offset, 8); EXPECT_TRUE(args[1].type.IsIntTy()); EXPECT_EQ(args[1].type.GetSize(), 8U); EXPECT_EQ(args[1].offset, 16); EXPECT_TRUE(args[2].type.IsStringTy()); EXPECT_EQ(args[2].type.GetSize(), 10U); EXPECT_EQ(args[2].offset, 24); EXPECT_TRUE(args[3].type.IsIntTy()); EXPECT_EQ(args[3].type.GetSize(), 8U); EXPECT_EQ(args[3].offset, 40); } TEST(codegen, probe_count) { MockBPFtrace bpftrace; EXPECT_CALL(bpftrace, add_probe(_, _, _, _)).Times(2); Driver driver(bpftrace); 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.ctx, bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::CodegenLLVM codegen(driver.ctx, bpftrace); codegen.generate_ir(); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/generate_codegen_includes.cmake000066400000000000000000000007531477746507000243260ustar00rootroot00000000000000# Combine all codegen tests into a single compilation unit to improve build # performance. https://github.com/bpftrace/bpftrace/issues/229 function(generate_codegen_includes output tests) file(REMOVE ${output}) file(WRITE ${output} "") foreach(test ${tests}) file(APPEND ${output} "#include \"${test}\"\n") endforeach() endfunction() separate_arguments(CODEGEN_SOURCES NATIVE_COMMAND ${CODEGEN_SOURCES}) generate_codegen_includes("${CODEGEN_INCLUDES_CPP}" "${CODEGEN_SOURCES}") bpftrace-0.23.2/tests/codegen/if_else_printf.cpp000066400000000000000000000004471477746507000216540ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_else_printf) { test("kprobe:f { if (pid > 10) { printf(\"hi\\n\"); } else " "{printf(\"hello\\n\")} }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/if_else_variable.cpp000066400000000000000000000004021477746507000221260ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_else_variable) { test("kprobe:f { if (1) { $s = 10 } else { $s = 20 } }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/if_nested_printf.cpp000066400000000000000000000004451477746507000222040ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_nested_printf) { test("kprobe:f { if (pid > 10000) { if (pid % 2 == 0) { printf(\"hi\\n\");} " "} }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/if_printf.cpp000066400000000000000000000004141477746507000206360ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_printf) { test("kprobe:f { if (pid > 10000) { printf(\"%d is high\\n\", pid); } }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/if_variable.cpp000066400000000000000000000003751477746507000211270ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, if_variable) { test("kprobe:f { let $x; if (1) { $x = 10 } $y = $x; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/int_propagation.cpp000066400000000000000000000003601477746507000220530ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, int_propagation) { test("kprobe:f { @x = 1234; @y = @x }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/intcast_assign_var.cpp000066400000000000000000000004601477746507000225400ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, intcast_retval) { // Make sure the result is truncated to 32 bit and sign extended to 64 test("kretprobe:f { @=(int32)retval }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/intcast_retval.cpp000066400000000000000000000004211477746507000216760ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, intcast_call) { // Casting should work inside a call test("kretprobe:f { @=sum((int32)retval) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/intptrcast_assign_var.cpp000066400000000000000000000004211477746507000232630ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { #ifdef __x86_64__ TEST(codegen, intptrcast_assign_var) { test("kretprobe:f { @=*(int8*)(reg(\"bp\")-1) }", NAME); } #endif } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/intptrcast_call.cpp000066400000000000000000000004671477746507000220540ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { #ifdef __x86_64__ TEST(codegen, intptrcast_call) { // Casting should work inside a call test("kretprobe:f { @=sum(*(int8*)(reg(\"bp\")-1)) }", NAME); } #endif } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/iter_dereference.cpp000066400000000000000000000003721477746507000221530ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, iter_dereference) { test("iter:task_file { @[ctx->meta->session_id] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/late_variable_decl.cpp000066400000000000000000000011361477746507000224410ustar00rootroot00000000000000#include "common.h" namespace bpftrace::test::codegen { TEST(codegen, late_variable_decl) { // This test is to ensure that late variable declarations in an outer scope // don't bleed into inner scopes earlier in the script. // All the $x variables below should get their own allocation test(R"( BEGIN { if (1) { $x = 1; } unroll(1) { $x = 2; } $i = 1; while($i) { --$i; $x = 3; } @map[16] = 32; for ($kv : @map) { $x = 4; } let $x = 5; })", NAME); } } // namespace bpftrace::test::codegen bpftrace-0.23.2/tests/codegen/llvm/000077500000000000000000000000001477746507000171255ustar00rootroot00000000000000bpftrace-0.23.2/tests/codegen/llvm/argN_rawtracepoint.ll000066400000000000000000000143411477746507000233120ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @rawtracepoint_sched_switch_1(ptr %0) section "s_rawtracepoint_sched_switch_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 0 %arg0 = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %arg0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "rawtracepoint_sched_switch_1", linkageName: "rawtracepoint_sched_switch_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/args_multiple_tracepoints.ll000066400000000000000000000163641477746507000247520ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_sched_sched_one_1(ptr %0) section "s_tracepoint_sched_sched_one_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i8, ptr %1, i64 8 %3 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %3, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 1 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 define i64 @tracepoint_sched_sched_two_2(ptr %0) section "s_tracepoint_sched_sched_two_2" !dbg !57 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i8, ptr %1, i64 16 %3 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %3, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 1 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "tracepoint_sched_sched_one_1", linkageName: "tracepoint_sched_sched_one_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) !57 = distinct !DISubprogram(name: "tracepoint_sched_sched_two_2", linkageName: "tracepoint_sched_sched_two_2", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !58) !58 = !{!59} !59 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/args_multiple_tracepoints_category_wild.ll000066400000000000000000000204451477746507000276610ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_sched_sched_one_1(ptr %0) section "s_tracepoint_sched_sched_one_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i8, ptr %1, i64 8 %3 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %3, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 1 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 define i64 @tracepoint_sched_sched_two_1(ptr %0) section "s_tracepoint_sched_sched_two_1" !dbg !57 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i8, ptr %1, i64 16 %3 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %3, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 1 } define i64 @tracepoint_sched_extra_sched_extra_1(ptr %0) section "s_tracepoint_sched_extra_sched_extra_1" !dbg !60 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i8, ptr %1, i64 24 %3 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %3, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 1 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "tracepoint_sched_sched_one_1", linkageName: "tracepoint_sched_sched_one_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) !57 = distinct !DISubprogram(name: "tracepoint_sched_sched_two_1", linkageName: "tracepoint_sched_sched_two_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !58) !58 = !{!59} !59 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !53) !60 = distinct !DISubprogram(name: "tracepoint_sched_extra_sched_extra_1", linkageName: "tracepoint_sched_extra_sched_extra_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !61) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !60, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/args_multiple_tracepoints_wild.ll000066400000000000000000000163641477746507000257710ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_sched_sched_one_1(ptr %0) section "s_tracepoint_sched_sched_one_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i8, ptr %1, i64 8 %3 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %3, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 1 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 define i64 @tracepoint_sched_sched_two_1(ptr %0) section "s_tracepoint_sched_sched_two_1" !dbg !57 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i8, ptr %1, i64 16 %3 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %3, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 1 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "tracepoint_sched_sched_one_1", linkageName: "tracepoint_sched_sched_one_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) !57 = distinct !DISubprogram(name: "tracepoint_sched_sched_two_1", linkageName: "tracepoint_sched_sched_two_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !58) !58 = !{!59} !59 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/array_integer_equal_comparison.ll000066400000000000000000000227541477746507000257440ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %exit_t = type <{ i64, i8 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %exit = alloca %exit_t, align 8 %n = alloca i32, align 4 %i = alloca i32, align 4 %arraycmp.result = alloca i1, align 1 %v2 = alloca i32, align 4 %v1 = alloca i32, align 4 %"$b" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$b") store i64 0, ptr %"$b", align 8 %"$a" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$a") store i64 0, ptr %"$a", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = inttoptr i64 %arg0 to ptr %4 = call ptr @llvm.preserve.static.offset(ptr %3) %5 = getelementptr i8, ptr %4, i64 0 %6 = ptrtoint ptr %5 to i64 store i64 %6, ptr %"$a", align 8 %7 = call ptr @llvm.preserve.static.offset(ptr %0) %8 = getelementptr i64, ptr %7, i64 14 %arg01 = load volatile i64, ptr %8, align 8 %9 = inttoptr i64 %arg01 to ptr %10 = call ptr @llvm.preserve.static.offset(ptr %9) %11 = getelementptr i8, ptr %10, i64 0 %12 = ptrtoint ptr %11 to i64 store i64 %12, ptr %"$b", align 8 %13 = load i64, ptr %"$a", align 8 %14 = load i64, ptr %"$b", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %v1) call void @llvm.lifetime.start.p0(i64 -1, ptr %v2) call void @llvm.lifetime.start.p0(i64 -1, ptr %arraycmp.result) store i1 true, ptr %arraycmp.result, align 1 %15 = inttoptr i64 %13 to ptr %16 = inttoptr i64 %14 to ptr call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %n) store i32 0, ptr %i, align 4 store i32 4, ptr %n, align 4 br label %while_cond if_body: ; preds = %arraycmp.done call void @llvm.lifetime.start.p0(i64 -1, ptr %exit) %17 = getelementptr %exit_t, ptr %exit, i64 0, i32 0 store i64 30000, ptr %17, align 8 %18 = getelementptr %exit_t, ptr %exit, i64 0, i32 1 store i8 0, ptr %18, align 1 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %exit, i64 9, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge if_end: ; preds = %deadcode, %arraycmp.done ret i64 0 while_cond: ; preds = %arraycmp.loop, %entry %19 = load i32, ptr %n, align 4 %20 = load i32, ptr %i, align 4 %size_check = icmp slt i32 %20, %19 br i1 %size_check, label %while_body, label %arraycmp.done, !llvm.loop !46 while_body: ; preds = %while_cond %21 = load i32, ptr %i, align 4 %22 = getelementptr [4 x i32], ptr %15, i32 0, i32 %21 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %v1, i32 4, ptr %22) %23 = load i32, ptr %v1, align 4 %24 = load i32, ptr %i, align 4 %25 = getelementptr [4 x i32], ptr %16, i32 0, i32 %24 %probe_read_kernel2 = call i64 inttoptr (i64 113 to ptr)(ptr %v2, i32 4, ptr %25) %26 = load i32, ptr %v2, align 4 %arraycmp.cmp = icmp ne i32 %23, %26 br i1 %arraycmp.cmp, label %arraycmp.false, label %arraycmp.loop arraycmp.false: ; preds = %while_body store i1 false, ptr %arraycmp.result, align 1 br label %arraycmp.done arraycmp.done: ; preds = %arraycmp.false, %while_cond %27 = load i1, ptr %arraycmp.result, align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %arraycmp.result) call void @llvm.lifetime.end.p0(i64 -1, ptr %v1) call void @llvm.lifetime.end.p0(i64 -1, ptr %v2) %28 = zext i1 %27 to i64 %true_cond = icmp ne i64 %28, 0 br i1 %true_cond, label %if_body, label %if_end arraycmp.loop: ; preds = %while_body %29 = load i32, ptr %i, align 4 %30 = add i32 %29, 1 store i32 %30, ptr %i, align 4 br label %while_cond event_loss_counter: ; preds = %if_body call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %if_body call void @llvm.lifetime.end.p0(i64 -1, ptr %exit) ret i64 0 lookup_success: ; preds = %event_loss_counter %31 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge deadcode: ; No predecessors! br label %if_end } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) !46 = distinct !{!46, !47} !47 = !{!"llvm.loop.unroll.disable"} bpftrace-0.23.2/tests/codegen/llvm/avg_cast.ll000066400000000000000000000261341477746507000212530ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %avg_stas_val = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !52 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %ret = alloca i64, align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %"@x_key1" = alloca i64, align 8 %avg_struct = alloca %avg_stas_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 0 %2 = load i64, ptr %1, align 8 %3 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 1 %4 = load i64, ptr %3, align 8 %5 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 0 %6 = add i64 %2, 2 store i64 %6, ptr %5, align 8 %7 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 1 %8 = add i64 1, %4 store i64 %8, ptr %7, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %avg_struct) %9 = getelementptr %avg_stas_val, ptr %avg_struct, i64 0, i32 0 store i64 2, ptr %9, align 8 %10 = getelementptr %avg_stas_val, ptr %avg_struct, i64 0, i32 1 store i64 1, ptr %10, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %avg_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %avg_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %lookup_success2, %lookup_merge %11 = load i32, ptr @num_cpus, align 4 %12 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %12, %11 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %13 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %"@x_key1", i32 %13) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success2, label %lookup_failure3 while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %ret) %14 = load i64, ptr %val_1, align 8 %is_negative_cond = icmp slt i64 %14, 0 br i1 %is_negative_cond, label %is_negative, label %is_positive lookup_success2: ; preds = %while_body %15 = getelementptr %avg_stas_val, ptr %lookup_percpu_elem, i64 0, i32 0 %16 = load i64, ptr %15, align 8 %17 = getelementptr %avg_stas_val, ptr %lookup_percpu_elem, i64 0, i32 1 %18 = load i64, ptr %17, align 8 %19 = load i64, ptr %val_1, align 8 %20 = add i64 %16, %19 store i64 %20, ptr %val_1, align 8 %21 = load i64, ptr %val_2, align 8 %22 = add i64 %18, %21 store i64 %22, ptr %val_2, align 8 %23 = load i32, ptr %i, align 4 %24 = add i32 %23, 1 store i32 %24, ptr %i, align 4 br label %while_cond lookup_failure3: ; preds = %while_body %25 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %25, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure error_success: ; preds = %lookup_failure3 br label %while_end error_failure: ; preds = %lookup_failure3 %26 = load i32, ptr %i, align 4 br label %while_end is_negative: ; preds = %while_end %27 = load i64, ptr %val_1, align 8 %28 = xor i64 %27, -1 %29 = add i64 %28, 1 %30 = load i64, ptr %val_2, align 8 %31 = udiv i64 %29, %30 %32 = sub i64 0, %31 store i64 %32, ptr %ret, align 8 br label %is_negative_merge_block is_positive: ; preds = %while_end %33 = load i64, ptr %val_2, align 8 %34 = load i64, ptr %val_1, align 8 %35 = udiv i64 %34, %33 store i64 %35, ptr %ret, align 8 br label %is_negative_merge_block is_negative_merge_block: ; preds = %is_positive, %is_negative %36 = load i64, ptr %ret, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %ret) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") store i64 %36, ptr %"$res", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !51} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !51 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !52 = !DIGlobalVariableExpression(var: !53, expr: !DIExpression()) !53 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !26, !40, !52} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !62) !58 = !DISubroutineType(types: !59) !59 = !{!18, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !61, size: 64) !61 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !62 = !{!63} !63 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/avg_cast_loop.ll000066400000000000000000000315321477746507000223020ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %avg_stas_val = type { i64, i64 } %int64_avg_t__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !57 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !62 { entry: %avg_struct = alloca %avg_stas_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 0 %2 = load i64, ptr %1, align 8 %3 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 1 %4 = load i64, ptr %3, align 8 %5 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 0 %6 = add i64 %2, 2 store i64 %6, ptr %5, align 8 %7 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 1 %8 = add i64 1, %4 store i64 %8, ptr %7, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %avg_struct) %9 = getelementptr %avg_stas_val, ptr %avg_struct, i64 0, i32 0 store i64 2, ptr %9, align 8 %10 = getelementptr %avg_stas_val, ptr %avg_struct, i64 0, i32 1 store i64 1, ptr %10, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %avg_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %avg_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_x, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !69 { %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %"$kv" = alloca %int64_avg_t__tuple_t, align 8 %ret = alloca i64, align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %key = load i64, ptr %1, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %lookup_success, %4 %5 = load i32, ptr @num_cpus, align 4 %6 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %6, %5 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %7 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %1, i32 %7) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %ret) %8 = load i64, ptr %val_1, align 8 %is_negative_cond = icmp slt i64 %8, 0 br i1 %is_negative_cond, label %is_negative, label %is_positive lookup_success: ; preds = %while_body %9 = getelementptr %avg_stas_val, ptr %lookup_percpu_elem, i64 0, i32 0 %10 = load i64, ptr %9, align 8 %11 = getelementptr %avg_stas_val, ptr %lookup_percpu_elem, i64 0, i32 1 %12 = load i64, ptr %11, align 8 %13 = load i64, ptr %val_1, align 8 %14 = add i64 %10, %13 store i64 %14, ptr %val_1, align 8 %15 = load i64, ptr %val_2, align 8 %16 = add i64 %12, %15 store i64 %16, ptr %val_2, align 8 %17 = load i32, ptr %i, align 4 %18 = add i32 %17, 1 store i32 %18, ptr %i, align 4 br label %while_cond lookup_failure: ; preds = %while_body %19 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %19, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure error_success: ; preds = %lookup_failure br label %while_end error_failure: ; preds = %lookup_failure %20 = load i32, ptr %i, align 4 br label %while_end is_negative: ; preds = %while_end %21 = load i64, ptr %val_1, align 8 %22 = xor i64 %21, -1 %23 = add i64 %22, 1 %24 = load i64, ptr %val_2, align 8 %25 = udiv i64 %23, %24 %26 = sub i64 0, %25 store i64 %26, ptr %ret, align 8 br label %is_negative_merge_block is_positive: ; preds = %while_end %27 = load i64, ptr %val_2, align 8 %28 = load i64, ptr %val_1, align 8 %29 = udiv i64 %28, %27 store i64 %29, ptr %ret, align 8 br label %is_negative_merge_block is_negative_merge_block: ; preds = %is_positive, %is_negative %30 = load i64, ptr %ret, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %ret) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %31 = getelementptr %int64_avg_t__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %31, align 8 %32 = getelementptr %int64_avg_t__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %30, ptr %32, align 8 %33 = getelementptr %int64_avg_t__tuple_t, ptr %"$kv", i32 0, i32 1 %34 = load i64, ptr %33, align 8 store i64 %34, ptr %"$res", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!59} !llvm.module.flags = !{!61} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !49, !54, !56} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !50, size: 64, offset: 64) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !52) !52 = !{!53} !53 = !DISubrange(count: 1, lowerBound: 0) !54 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !55, size: 64, offset: 128) !55 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !56 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !57 = !DIGlobalVariableExpression(var: !58, expr: !DIExpression()) !58 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !59 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !60) !60 = !{!0, !26, !40, !57} !61 = !{i32 2, !"Debug Info Version", i32 3} !62 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !59, retainedNodes: !67) !63 = !DISubroutineType(types: !64) !64 = !{!18, !65} !65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) !66 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !67 = !{!68} !68 = !DILocalVariable(name: "ctx", arg: 1, scope: !62, file: !2, type: !65) !69 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !70, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !59, retainedNodes: !72) !70 = !DISubroutineType(types: !71) !71 = !{!18, !65, !65, !65, !65} !72 = !{!73, !74, !75, !76} !73 = !DILocalVariable(name: "map", arg: 1, scope: !69, file: !2, type: !65) !74 = !DILocalVariable(name: "key", arg: 2, scope: !69, file: !2, type: !65) !75 = !DILocalVariable(name: "value", arg: 3, scope: !69, file: !2, type: !65) !76 = !DILocalVariable(name: "ctx", arg: 4, scope: !69, file: !2, type: !65) bpftrace-0.23.2/tests/codegen/llvm/basic_while_loop.ll000066400000000000000000000143201477746507000227600ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @interval_s_1_1(ptr %0) section "s_interval_s_1_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %"$a" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$a") store i64 0, ptr %"$a", align 8 store i64 1, ptr %"$a", align 8 br label %while_cond while_cond: ; preds = %while_body, %entry %1 = load i64, ptr %"$a", align 8 %2 = icmp sle i64 %1, 150 %true_cond = icmp ne i1 %2, false br i1 %true_cond, label %while_body, label %while_end, !llvm.loop !52 while_body: ; preds = %while_cond %3 = load i64, ptr %"$a", align 8 %4 = add i64 %3, 1 store i64 %4, ptr %"$a", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 %3, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") br label %while_cond while_end: ; preds = %while_cond ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "interval_s_1_1", linkageName: "interval_s_1_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) !52 = distinct !{!52, !53} !53 = !{!"llvm.loop.unroll.disable"} bpftrace-0.23.2/tests/codegen/llvm/binop_int_promotion.ll000066400000000000000000000106651477746507000235550ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kretprobe_f_1(ptr %0) section "s_kretprobe_f_1" !dbg !39 { entry: %"$x" = alloca i32, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i32 0, ptr %"$x", align 4 store i32 5, ptr %"$x", align 4 %1 = load i32, ptr %"$x", align 4 %2 = add i32 %1, 1 store i32 %2, ptr %"$x", align 4 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kretprobe_f_1", linkageName: "kretprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/bitshift_left.ll000066400000000000000000000127351477746507000223140ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1024, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/bitshift_right.ll000066400000000000000000000127321477746507000224740ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 2, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/bitwise_not.ll000066400000000000000000000127201477746507000220060ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 -11, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_arg.ll000066400000000000000000000154341477746507000217640ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %arg0, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %3 = call ptr @llvm.preserve.static.offset(ptr %0) %4 = getelementptr i64, ptr %3, i64 12 %arg2 = load volatile i64, ptr %4, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") store i64 %arg2, ptr %"@y_val", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/builtin_comm.ll000066400000000000000000000142001477746507000221340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@x_key" = alloca i64, align 8 %comm = alloca [16 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %comm) call void @llvm.memset.p0.i64(ptr align 1 %comm, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to ptr)(ptr %comm, i64 16) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %comm, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %comm) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 16, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/builtin_cpid.ll000066400000000000000000000127201477746507000221250ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1337, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_cpu.ll000066400000000000000000000130271477746507000217760ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_cpu_id, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_ctx.ll000066400000000000000000000127731477746507000220140ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %cast = zext ptr %0 to i64 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %cast, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_ctx_field.ll000066400000000000000000000264401477746507000231530ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %"struct map_t.3" = type { ptr, ptr, ptr, ptr } %"struct map_t.4" = type { ptr, ptr } %"struct map_t.5" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_a = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_b = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @AT_c = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @AT_d = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !20 @AT_e = dso_local global %"struct map_t.3" zeroinitializer, section ".maps", !dbg !22 @ringbuf = dso_local global %"struct map_t.4" zeroinitializer, section ".maps", !dbg !32 @event_loss_counter = dso_local global %"struct map_t.5" zeroinitializer, section ".maps", !dbg !46 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !61 { entry: %"@e_key" = alloca i64, align 8 %"struct x.e" = alloca [4 x i8], align 1 %"@d_val" = alloca i64, align 8 %"@d_key" = alloca i64, align 8 %"struct c.c" = alloca i8, align 1 %"@c_val" = alloca i64, align 8 %"@c_key" = alloca i64, align 8 %"@b_val" = alloca i64, align 8 %"@b_key" = alloca i64, align 8 %"@a_val" = alloca i64, align 8 %"@a_key" = alloca i64, align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 store ptr %0, ptr %"$x", align 8 %1 = load i64, ptr %"$x", align 8 %2 = inttoptr i64 %1 to ptr %3 = call ptr @llvm.preserve.static.offset(ptr %2) %4 = getelementptr i8, ptr %3, i64 0 %5 = load volatile i64, ptr %4, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@a_key") store i64 0, ptr %"@a_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@a_val") store i64 %5, ptr %"@a_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_a, ptr %"@a_key", ptr %"@a_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@a_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@a_key") %6 = load i64, ptr %"$x", align 8 %7 = inttoptr i64 %6 to ptr %8 = call ptr @llvm.preserve.static.offset(ptr %7) %9 = getelementptr i8, ptr %8, i64 8 %10 = ptrtoint ptr %9 to i64 %11 = inttoptr i64 %10 to ptr %12 = call ptr @llvm.preserve.static.offset(ptr %11) %13 = getelementptr i8, ptr %12, i64 0 %14 = load volatile i16, ptr %13, align 2 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@b_key") store i64 0, ptr %"@b_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@b_val") %15 = sext i16 %14 to i64 store i64 %15, ptr %"@b_val", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_b, ptr %"@b_key", ptr %"@b_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@b_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@b_key") %16 = load i64, ptr %"$x", align 8 %17 = inttoptr i64 %16 to ptr %18 = call ptr @llvm.preserve.static.offset(ptr %17) %19 = getelementptr i8, ptr %18, i64 16 %20 = call ptr @llvm.preserve.static.offset(ptr %19) %21 = getelementptr i8, ptr %20, i64 0 %22 = load volatile i8, ptr %21, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@c_key") store i64 0, ptr %"@c_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@c_val") %23 = sext i8 %22 to i64 store i64 %23, ptr %"@c_val", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_c, ptr %"@c_key", ptr %"@c_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@c_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@c_key") %24 = load i64, ptr %"$x", align 8 %25 = inttoptr i64 %24 to ptr %26 = call ptr @llvm.preserve.static.offset(ptr %25) %27 = getelementptr i8, ptr %26, i64 24 %28 = load volatile i64, ptr %27, align 8 %29 = inttoptr i64 %28 to ptr %30 = call ptr @llvm.preserve.static.offset(ptr %29) %31 = getelementptr i8, ptr %30, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct c.c") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct c.c", i32 1, ptr %31) %32 = load i8, ptr %"struct c.c", align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct c.c") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@d_key") store i64 0, ptr %"@d_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@d_val") %33 = sext i8 %32 to i64 store i64 %33, ptr %"@d_val", align 8 %update_elem3 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_d, ptr %"@d_key", ptr %"@d_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@d_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@d_key") %34 = load i64, ptr %"$x", align 8 %35 = inttoptr i64 %34 to ptr %36 = call ptr @llvm.preserve.static.offset(ptr %35) %37 = getelementptr i8, ptr %36, i64 32 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct x.e") %probe_read_kernel4 = call i64 inttoptr (i64 113 to ptr)(ptr %"struct x.e", i32 4, ptr %37) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@e_key") store i64 0, ptr %"@e_key", align 8 %update_elem5 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_e, ptr %"@e_key", ptr %"struct x.e", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@e_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct x.e") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!58} !llvm.module.flags = !{!60} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_a", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_b", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "AT_c", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "AT_d", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !22 = !DIGlobalVariableExpression(var: !23, expr: !DIExpression()) !23 = distinct !DIGlobalVariable(name: "AT_e", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !24 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !25) !25 = !{!5, !11, !12, !26} !26 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !27, size: 64, offset: 192) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !29, size: 32, elements: !30) !29 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !30 = !{!31} !31 = !DISubrange(count: 4, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !35) !35 = !{!36, !41} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 27, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !42, size: 64, offset: 64) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !44) !44 = !{!45} !45 = !DISubrange(count: 262144, lowerBound: 0) !46 = !DIGlobalVariableExpression(var: !47, expr: !DIExpression()) !47 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !48, isLocal: false, isDefinition: true) !48 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !49) !49 = !{!50, !11, !55, !15} !50 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !51, size: 64) !51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !52, size: 64) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !53) !53 = !{!54} !54 = !DISubrange(count: 2, lowerBound: 0) !55 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !56, size: 64, offset: 128) !56 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !57, size: 64) !57 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !58 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !59) !59 = !{!0, !16, !18, !20, !22, !32, !46} !60 = !{i32 2, !"Debug Info Version", i32 3} !61 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !62, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !58, retainedNodes: !65) !62 = !DISubroutineType(types: !63) !63 = !{!14, !64} !64 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !65 = !{!66} !66 = !DILocalVariable(name: "ctx", arg: 1, scope: !61, file: !2, type: !64) bpftrace-0.23.2/tests/codegen/llvm/builtin_curtask.ll000066400000000000000000000130341477746507000226610ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_cur_task = call i64 inttoptr (i64 35 to ptr)() call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_cur_task, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_elapsed.ll000066400000000000000000000156241477746507000226310ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @elapsed = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @interval_s_1_1(ptr %0) section "s_interval_s_1_1" !dbg !47 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %elapsed_key = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %elapsed_key) store i64 0, ptr %elapsed_key, align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @elapsed, ptr %elapsed_key) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) %get_ns = call i64 inttoptr (i64 125 to ptr)() %3 = sub i64 %get_ns, %2 call void @llvm.lifetime.end.p0(i64 -1, ptr %elapsed_key) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 %3, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "elapsed", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "interval_s_1_1", linkageName: "interval_s_1_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/builtin_func_fentry.ll000066400000000000000000000131251477746507000235300ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @fentry_mock_vmlinux_f_1(ptr %0) section "s_fentry_mock_vmlinux_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_func_ip = call i64 inttoptr (i64 173 to ptr)(ptr %0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_func_ip, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "fentry_mock_vmlinux_f_1", linkageName: "fentry_mock_vmlinux_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_func_kprobe.ll000066400000000000000000000135331477746507000235060ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 16 %func = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %func, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_func_kretprobe.ll000066400000000000000000000130551477746507000242200ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kretprobe_f_1(ptr %0) section "s_kretprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_func_ip = call i64 inttoptr (i64 173 to ptr)(ptr %0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_func_ip, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kretprobe_f_1", linkageName: "kretprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_func_uprobe.ll000066400000000000000000000150551477746507000235210ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %usym_t = type { i64, i32, i32 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @uprobe__bin_sh_f_1(ptr %0) section "s_uprobe__bin_sh_f_1" !dbg !51 { entry: %"@x_key" = alloca i64, align 8 %usym = alloca %usym_t, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 16 %func = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %usym) %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %3 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %3 to i32 %4 = getelementptr %usym_t, ptr %usym, i64 0, i32 0 %5 = getelementptr %usym_t, ptr %usym, i64 0, i32 1 %6 = getelementptr %usym_t, ptr %usym, i64 0, i32 2 store i64 %func, ptr %4, align 8 store i32 %pid, ptr %5, align 4 store i32 0, ptr %6, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %usym, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %usym) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 16, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "uprobe__bin_sh_f_1", linkageName: "uprobe__bin_sh_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/builtin_func_uretprobe.ll000066400000000000000000000143771477746507000242420ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %usym_t = type { i64, i32, i32 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @uretprobe__bin_sh_f_1(ptr %0) section "s_uretprobe__bin_sh_f_1" !dbg !51 { entry: %"@x_key" = alloca i64, align 8 %usym = alloca %usym_t, align 8 %get_func_ip = call i64 inttoptr (i64 173 to ptr)(ptr %0) call void @llvm.lifetime.start.p0(i64 -1, ptr %usym) %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = getelementptr %usym_t, ptr %usym, i64 0, i32 0 %3 = getelementptr %usym_t, ptr %usym, i64 0, i32 1 %4 = getelementptr %usym_t, ptr %usym, i64 0, i32 2 store i64 %get_func_ip, ptr %2, align 8 store i32 %pid, ptr %3, align 4 store i32 0, ptr %4, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %usym, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %usym) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 16, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "uretprobe__bin_sh_f_1", linkageName: "uretprobe__bin_sh_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/builtin_func_wild.ll000066400000000000000000000135531477746507000231650ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_sys___1(ptr %0) section "s_kprobe_sys___1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 16 %func = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %func, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_sys___1", linkageName: "kprobe_sys___1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_jiffies.ll000066400000000000000000000130271477746507000226260ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %jiffies64 = call i64 inttoptr (i64 118 to ptr)() call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %jiffies64, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_kstack.ll000066400000000000000000000325441477746507000224740ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct map_t.2" = type { ptr, ptr } %"struct map_t.3" = type { ptr, ptr, ptr, ptr } %kstack_key = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @stack_bpftrace_127 = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @stack_scratch = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !45 @ringbuf = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !57 @event_loss_counter = dso_local global %"struct map_t.3" zeroinitializer, section ".maps", !dbg !71 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !84 { entry: %"@x_key" = alloca i64, align 8 %lookup_stack_scratch_key = alloca i32, align 4 %stack_key = alloca %kstack_key, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key) call void @llvm.memset.p0.i64(ptr align 1 %stack_key, i8 0, i64 16, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key) store i32 0, ptr %lookup_stack_scratch_key, align 4 %lookup_stack_scratch_map = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key) %lookup_stack_scratch_cond = icmp ne ptr %lookup_stack_scratch_map, null br i1 %lookup_stack_scratch_cond, label %lookup_stack_scratch_merge, label %lookup_stack_scratch_failure stack_scratch_failure: ; preds = %lookup_stack_scratch_failure br label %merge_block merge_block: ; preds = %stack_scratch_failure, %get_stack_success, %get_stack_fail call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %stack_key, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 lookup_stack_scratch_failure: ; preds = %entry br label %stack_scratch_failure lookup_stack_scratch_merge: ; preds = %entry %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map, i32 1016, ptr null) %get_stack = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map, i32 1016, i64 0) %1 = icmp sge i64 %get_stack, 0 br i1 %1, label %get_stack_success, label %get_stack_fail get_stack_success: ; preds = %lookup_stack_scratch_merge %2 = udiv i64 %get_stack, 8 %3 = getelementptr %kstack_key, ptr %stack_key, i64 0, i32 1 store i64 %2, ptr %3, align 8 %4 = trunc i64 %2 to i8 %murmur_hash_2 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map, i8 %4, i64 1) %5 = getelementptr %kstack_key, ptr %stack_key, i64 0, i32 0 store i64 %murmur_hash_2, ptr %5, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_127, ptr %stack_key, ptr %lookup_stack_scratch_map, i64 0) br label %merge_block get_stack_fail: ; preds = %lookup_stack_scratch_merge br label %merge_block } ; Function Attrs: alwaysinline define internal i64 @murmur_hash_2(ptr %0, i8 %1, i64 %2) #1 section "helpers" { entry: %k = alloca i64, align 8 %i = alloca i8, align 1 %id = alloca i64, align 8 %seed_addr = alloca i64, align 8 %nr_stack_frames_addr = alloca i8, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %id) call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %k) store i8 %1, ptr %nr_stack_frames_addr, align 1 store i64 %2, ptr %seed_addr, align 8 %3 = load i8, ptr %nr_stack_frames_addr, align 1 %4 = zext i8 %3 to i64 %5 = mul i64 %4, -4132994306676758123 %6 = load i64, ptr %seed_addr, align 8 %7 = xor i64 %6, %5 store i64 %7, ptr %id, align 8 store i8 0, ptr %i, align 1 br label %while_cond while_cond: ; preds = %while_body, %entry %8 = load i8, ptr %nr_stack_frames_addr, align 1 %9 = load i8, ptr %i, align 1 %length.cmp = icmp ult i8 %9, %8 br i1 %length.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %10 = load i8, ptr %i, align 1 %11 = getelementptr i64, ptr %0, i8 %10 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %k, align 8 %13 = load i64, ptr %k, align 8 %14 = mul i64 %13, -4132994306676758123 store i64 %14, ptr %k, align 8 %15 = load i64, ptr %k, align 8 %16 = lshr i64 %15, 47 %17 = load i64, ptr %k, align 8 %18 = xor i64 %17, %16 store i64 %18, ptr %k, align 8 %19 = load i64, ptr %k, align 8 %20 = mul i64 %19, -4132994306676758123 store i64 %20, ptr %k, align 8 %21 = load i64, ptr %k, align 8 %22 = load i64, ptr %id, align 8 %23 = xor i64 %22, %21 store i64 %23, ptr %id, align 8 %24 = load i64, ptr %id, align 8 %25 = mul i64 %24, -4132994306676758123 store i64 %25, ptr %id, align 8 %26 = load i8, ptr %i, align 1 %27 = add i8 %26, 1 store i8 %27, ptr %i, align 1 br label %while_cond while_end: ; preds = %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %i) call void @llvm.lifetime.end.p0(i64 -1, ptr %k) %28 = load i64, ptr %id, align 8 %zero_cond = icmp eq i64 %28, 0 br i1 %zero_cond, label %if_zero, label %if_end if_zero: ; preds = %while_end store i64 1, ptr %id, align 8 br label %if_end if_end: ; preds = %if_zero, %while_end %29 = load i64, ptr %id, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %id) ret i64 %29 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!81} !llvm.module.flags = !{!83} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 16, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "stack_bpftrace_127", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !24) !24 = !{!25, !30, !35, !40} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 288, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 9, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 4194304, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 131072, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !36, size: 64, offset: 128) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 96, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 12, lowerBound: 0) !40 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !41, size: 64, offset: 192) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 8128, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 127, lowerBound: 0) !45 = !DIGlobalVariableExpression(var: !46, expr: !DIExpression()) !46 = distinct !DIGlobalVariable(name: "stack_scratch", linkageName: "global", scope: !2, file: !2, type: !47, isLocal: false, isDefinition: true) !47 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !48) !48 = !{!49, !11, !54, !40} !49 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !50, size: 64) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !52) !52 = !{!53} !53 = !DISubrange(count: 6, lowerBound: 0) !54 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !55, size: 64, offset: 128) !55 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !56, size: 64) !56 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !57 = !DIGlobalVariableExpression(var: !58, expr: !DIExpression()) !58 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !59, isLocal: false, isDefinition: true) !59 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !60) !60 = !{!61, !66} !61 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !62, size: 64) !62 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !63, size: 64) !63 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !64) !64 = !{!65} !65 = !DISubrange(count: 27, lowerBound: 0) !66 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !67, size: 64, offset: 64) !67 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !68, size: 64) !68 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !69) !69 = !{!70} !70 = !DISubrange(count: 262144, lowerBound: 0) !71 = !DIGlobalVariableExpression(var: !72, expr: !DIExpression()) !72 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !73, isLocal: false, isDefinition: true) !73 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !74) !74 = !{!75, !11, !54, !80} !75 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !76, size: 64) !76 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !77, size: 64) !77 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !78) !78 = !{!79} !79 = !DISubrange(count: 2, lowerBound: 0) !80 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !81 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !82) !82 = !{!0, !21, !45, !57, !71} !83 = !{i32 2, !"Debug Info Version", i32 3} !84 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !85, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !81, retainedNodes: !88) !85 = !DISubroutineType(types: !86) !86 = !{!14, !87} !87 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !88 = !{!89} !89 = !DILocalVariable(name: "ctx", arg: 1, scope: !84, file: !2, type: !87) bpftrace-0.23.2/tests/codegen/llvm/builtin_nsecs.ll000066400000000000000000000130211477746507000223140ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_ns = call i64 inttoptr (i64 125 to ptr)() call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_ns, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_numaid.ll000066400000000000000000000130321477746507000224600ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_numa_id = call i64 inttoptr (i64 42 to ptr)() call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_numa_id, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_pid_tid.ll000066400000000000000000000150331477746507000226220ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %2 = zext i32 %pid to i64 store i64 %2, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %get_pid_tgid1 = call i64 inttoptr (i64 14 to ptr)() %tid = trunc i64 %get_pid_tgid1 to i32 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") %3 = zext i32 %tid to i64 store i64 %3, ptr %"@y_val", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/builtin_pid_tid_namespace.ll000066400000000000000000000161741477746507000246450ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %bpf_pidns_info = type { i32, i32 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %bpf_pidns_info1 = alloca %bpf_pidns_info, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %bpf_pidns_info = alloca %bpf_pidns_info, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %bpf_pidns_info) %get_ns_pid_tgid = call i64 inttoptr (i64 120 to ptr)(i64 0, i64 4026531857, ptr %bpf_pidns_info, i32 8) %1 = getelementptr %bpf_pidns_info, ptr %bpf_pidns_info, i32 0, i32 0 %2 = load i32, ptr %1, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %bpf_pidns_info) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %3 = zext i32 %2 to i64 store i64 %3, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %bpf_pidns_info1) %get_ns_pid_tgid2 = call i64 inttoptr (i64 120 to ptr)(i64 0, i64 4026531857, ptr %bpf_pidns_info1, i32 8) %4 = getelementptr %bpf_pidns_info, ptr %bpf_pidns_info1, i32 0, i32 1 %5 = load i32, ptr %4, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %bpf_pidns_info1) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") %6 = zext i32 %5 to i64 store i64 %6, ptr %"@y_val", align 8 %update_elem3 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/builtin_probe.ll000066400000000000000000000133371477746507000223220ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !33 @"tracepoint:sched:sched_one" = global [27 x i8] c"tracepoint:sched:sched_one\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_sched_sched_one_1(ptr %0) section "s_tracepoint_sched_sched_one_1" !dbg !49 { entry: %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr @"tracepoint:sched:sched_one", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 1 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 216, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 27, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !28} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !19) !28 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !29, size: 64, offset: 64) !29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64) !30 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !31) !31 = !{!32} !32 = !DISubrange(count: 262144, lowerBound: 0) !33 = !DIGlobalVariableExpression(var: !34, expr: !DIExpression()) !34 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !35 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !36) !36 = !{!37, !11, !42, !45} !37 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !38, size: 64) !38 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !39, size: 64) !39 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !40) !40 = !{!41} !41 = !DISubrange(count: 2, lowerBound: 0) !42 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !43, size: 64, offset: 128) !43 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !44, size: 64) !44 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !45 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !21, !33} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "tracepoint_sched_sched_one_1", linkageName: "tracepoint_sched_sched_one_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!14, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/builtin_probe_comparison.ll000066400000000000000000000522141477746507000245510ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @"tracepoint:sched:sched_one" = global [27 x i8] c"tracepoint:sched:sched_one\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_sched_sched_one_1(ptr %0) section "s_tracepoint_sched_sched_one_1" !dbg !39 { entry: %strcmp.result = alloca i1, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %strcmp.result) store i1 false, ptr %strcmp.result, align 1 %1 = load i8, ptr @"tracepoint:sched:sched_one", align 1 %2 = load i8, ptr @"tracepoint:sched:sched_one", align 1 %strcmp.cmp = icmp ne i8 %1, %2 br i1 %strcmp.cmp, label %strcmp.false, label %strcmp.loop_null_cmp if_body: ; preds = %strcmp.false br label %if_end if_end: ; preds = %if_body, %strcmp.false ret i64 1 strcmp.false: ; preds = %strcmp.done, %strcmp.loop97, %strcmp.loop93, %strcmp.loop89, %strcmp.loop85, %strcmp.loop81, %strcmp.loop77, %strcmp.loop73, %strcmp.loop69, %strcmp.loop65, %strcmp.loop61, %strcmp.loop57, %strcmp.loop53, %strcmp.loop49, %strcmp.loop45, %strcmp.loop41, %strcmp.loop37, %strcmp.loop33, %strcmp.loop29, %strcmp.loop25, %strcmp.loop21, %strcmp.loop17, %strcmp.loop13, %strcmp.loop9, %strcmp.loop5, %strcmp.loop1, %strcmp.loop, %entry %3 = load i1, ptr %strcmp.result, align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %strcmp.result) %4 = zext i1 %3 to i64 %true_cond = icmp ne i64 %4, 0 br i1 %true_cond, label %if_body, label %if_end strcmp.done: ; preds = %strcmp.loop101, %strcmp.loop_null_cmp102, %strcmp.loop_null_cmp98, %strcmp.loop_null_cmp94, %strcmp.loop_null_cmp90, %strcmp.loop_null_cmp86, %strcmp.loop_null_cmp82, %strcmp.loop_null_cmp78, %strcmp.loop_null_cmp74, %strcmp.loop_null_cmp70, %strcmp.loop_null_cmp66, %strcmp.loop_null_cmp62, %strcmp.loop_null_cmp58, %strcmp.loop_null_cmp54, %strcmp.loop_null_cmp50, %strcmp.loop_null_cmp46, %strcmp.loop_null_cmp42, %strcmp.loop_null_cmp38, %strcmp.loop_null_cmp34, %strcmp.loop_null_cmp30, %strcmp.loop_null_cmp26, %strcmp.loop_null_cmp22, %strcmp.loop_null_cmp18, %strcmp.loop_null_cmp14, %strcmp.loop_null_cmp10, %strcmp.loop_null_cmp6, %strcmp.loop_null_cmp2, %strcmp.loop_null_cmp store i1 true, ptr %strcmp.result, align 1 br label %strcmp.false strcmp.loop: ; preds = %strcmp.loop_null_cmp %5 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 1), align 1 %6 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 1), align 1 %strcmp.cmp3 = icmp ne i8 %5, %6 br i1 %strcmp.cmp3, label %strcmp.false, label %strcmp.loop_null_cmp2 strcmp.loop_null_cmp: ; preds = %entry %strcmp.cmp_null = icmp eq i8 %1, 0 br i1 %strcmp.cmp_null, label %strcmp.done, label %strcmp.loop strcmp.loop1: ; preds = %strcmp.loop_null_cmp2 %7 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 2), align 1 %8 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 2), align 1 %strcmp.cmp7 = icmp ne i8 %7, %8 br i1 %strcmp.cmp7, label %strcmp.false, label %strcmp.loop_null_cmp6 strcmp.loop_null_cmp2: ; preds = %strcmp.loop %strcmp.cmp_null4 = icmp eq i8 %5, 0 br i1 %strcmp.cmp_null4, label %strcmp.done, label %strcmp.loop1 strcmp.loop5: ; preds = %strcmp.loop_null_cmp6 %9 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 3), align 1 %10 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 3), align 1 %strcmp.cmp11 = icmp ne i8 %9, %10 br i1 %strcmp.cmp11, label %strcmp.false, label %strcmp.loop_null_cmp10 strcmp.loop_null_cmp6: ; preds = %strcmp.loop1 %strcmp.cmp_null8 = icmp eq i8 %7, 0 br i1 %strcmp.cmp_null8, label %strcmp.done, label %strcmp.loop5 strcmp.loop9: ; preds = %strcmp.loop_null_cmp10 %11 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 4), align 1 %12 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 4), align 1 %strcmp.cmp15 = icmp ne i8 %11, %12 br i1 %strcmp.cmp15, label %strcmp.false, label %strcmp.loop_null_cmp14 strcmp.loop_null_cmp10: ; preds = %strcmp.loop5 %strcmp.cmp_null12 = icmp eq i8 %9, 0 br i1 %strcmp.cmp_null12, label %strcmp.done, label %strcmp.loop9 strcmp.loop13: ; preds = %strcmp.loop_null_cmp14 %13 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 5), align 1 %14 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 5), align 1 %strcmp.cmp19 = icmp ne i8 %13, %14 br i1 %strcmp.cmp19, label %strcmp.false, label %strcmp.loop_null_cmp18 strcmp.loop_null_cmp14: ; preds = %strcmp.loop9 %strcmp.cmp_null16 = icmp eq i8 %11, 0 br i1 %strcmp.cmp_null16, label %strcmp.done, label %strcmp.loop13 strcmp.loop17: ; preds = %strcmp.loop_null_cmp18 %15 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 6), align 1 %16 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 6), align 1 %strcmp.cmp23 = icmp ne i8 %15, %16 br i1 %strcmp.cmp23, label %strcmp.false, label %strcmp.loop_null_cmp22 strcmp.loop_null_cmp18: ; preds = %strcmp.loop13 %strcmp.cmp_null20 = icmp eq i8 %13, 0 br i1 %strcmp.cmp_null20, label %strcmp.done, label %strcmp.loop17 strcmp.loop21: ; preds = %strcmp.loop_null_cmp22 %17 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 7), align 1 %18 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 7), align 1 %strcmp.cmp27 = icmp ne i8 %17, %18 br i1 %strcmp.cmp27, label %strcmp.false, label %strcmp.loop_null_cmp26 strcmp.loop_null_cmp22: ; preds = %strcmp.loop17 %strcmp.cmp_null24 = icmp eq i8 %15, 0 br i1 %strcmp.cmp_null24, label %strcmp.done, label %strcmp.loop21 strcmp.loop25: ; preds = %strcmp.loop_null_cmp26 %19 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 8), align 1 %20 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 8), align 1 %strcmp.cmp31 = icmp ne i8 %19, %20 br i1 %strcmp.cmp31, label %strcmp.false, label %strcmp.loop_null_cmp30 strcmp.loop_null_cmp26: ; preds = %strcmp.loop21 %strcmp.cmp_null28 = icmp eq i8 %17, 0 br i1 %strcmp.cmp_null28, label %strcmp.done, label %strcmp.loop25 strcmp.loop29: ; preds = %strcmp.loop_null_cmp30 %21 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 9), align 1 %22 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 9), align 1 %strcmp.cmp35 = icmp ne i8 %21, %22 br i1 %strcmp.cmp35, label %strcmp.false, label %strcmp.loop_null_cmp34 strcmp.loop_null_cmp30: ; preds = %strcmp.loop25 %strcmp.cmp_null32 = icmp eq i8 %19, 0 br i1 %strcmp.cmp_null32, label %strcmp.done, label %strcmp.loop29 strcmp.loop33: ; preds = %strcmp.loop_null_cmp34 %23 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 10), align 1 %24 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 10), align 1 %strcmp.cmp39 = icmp ne i8 %23, %24 br i1 %strcmp.cmp39, label %strcmp.false, label %strcmp.loop_null_cmp38 strcmp.loop_null_cmp34: ; preds = %strcmp.loop29 %strcmp.cmp_null36 = icmp eq i8 %21, 0 br i1 %strcmp.cmp_null36, label %strcmp.done, label %strcmp.loop33 strcmp.loop37: ; preds = %strcmp.loop_null_cmp38 %25 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 11), align 1 %26 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 11), align 1 %strcmp.cmp43 = icmp ne i8 %25, %26 br i1 %strcmp.cmp43, label %strcmp.false, label %strcmp.loop_null_cmp42 strcmp.loop_null_cmp38: ; preds = %strcmp.loop33 %strcmp.cmp_null40 = icmp eq i8 %23, 0 br i1 %strcmp.cmp_null40, label %strcmp.done, label %strcmp.loop37 strcmp.loop41: ; preds = %strcmp.loop_null_cmp42 %27 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 12), align 1 %28 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 12), align 1 %strcmp.cmp47 = icmp ne i8 %27, %28 br i1 %strcmp.cmp47, label %strcmp.false, label %strcmp.loop_null_cmp46 strcmp.loop_null_cmp42: ; preds = %strcmp.loop37 %strcmp.cmp_null44 = icmp eq i8 %25, 0 br i1 %strcmp.cmp_null44, label %strcmp.done, label %strcmp.loop41 strcmp.loop45: ; preds = %strcmp.loop_null_cmp46 %29 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 13), align 1 %30 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 13), align 1 %strcmp.cmp51 = icmp ne i8 %29, %30 br i1 %strcmp.cmp51, label %strcmp.false, label %strcmp.loop_null_cmp50 strcmp.loop_null_cmp46: ; preds = %strcmp.loop41 %strcmp.cmp_null48 = icmp eq i8 %27, 0 br i1 %strcmp.cmp_null48, label %strcmp.done, label %strcmp.loop45 strcmp.loop49: ; preds = %strcmp.loop_null_cmp50 %31 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 14), align 1 %32 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 14), align 1 %strcmp.cmp55 = icmp ne i8 %31, %32 br i1 %strcmp.cmp55, label %strcmp.false, label %strcmp.loop_null_cmp54 strcmp.loop_null_cmp50: ; preds = %strcmp.loop45 %strcmp.cmp_null52 = icmp eq i8 %29, 0 br i1 %strcmp.cmp_null52, label %strcmp.done, label %strcmp.loop49 strcmp.loop53: ; preds = %strcmp.loop_null_cmp54 %33 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 15), align 1 %34 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 15), align 1 %strcmp.cmp59 = icmp ne i8 %33, %34 br i1 %strcmp.cmp59, label %strcmp.false, label %strcmp.loop_null_cmp58 strcmp.loop_null_cmp54: ; preds = %strcmp.loop49 %strcmp.cmp_null56 = icmp eq i8 %31, 0 br i1 %strcmp.cmp_null56, label %strcmp.done, label %strcmp.loop53 strcmp.loop57: ; preds = %strcmp.loop_null_cmp58 %35 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 16), align 1 %36 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 16), align 1 %strcmp.cmp63 = icmp ne i8 %35, %36 br i1 %strcmp.cmp63, label %strcmp.false, label %strcmp.loop_null_cmp62 strcmp.loop_null_cmp58: ; preds = %strcmp.loop53 %strcmp.cmp_null60 = icmp eq i8 %33, 0 br i1 %strcmp.cmp_null60, label %strcmp.done, label %strcmp.loop57 strcmp.loop61: ; preds = %strcmp.loop_null_cmp62 %37 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 17), align 1 %38 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 17), align 1 %strcmp.cmp67 = icmp ne i8 %37, %38 br i1 %strcmp.cmp67, label %strcmp.false, label %strcmp.loop_null_cmp66 strcmp.loop_null_cmp62: ; preds = %strcmp.loop57 %strcmp.cmp_null64 = icmp eq i8 %35, 0 br i1 %strcmp.cmp_null64, label %strcmp.done, label %strcmp.loop61 strcmp.loop65: ; preds = %strcmp.loop_null_cmp66 %39 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 18), align 1 %40 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 18), align 1 %strcmp.cmp71 = icmp ne i8 %39, %40 br i1 %strcmp.cmp71, label %strcmp.false, label %strcmp.loop_null_cmp70 strcmp.loop_null_cmp66: ; preds = %strcmp.loop61 %strcmp.cmp_null68 = icmp eq i8 %37, 0 br i1 %strcmp.cmp_null68, label %strcmp.done, label %strcmp.loop65 strcmp.loop69: ; preds = %strcmp.loop_null_cmp70 %41 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 19), align 1 %42 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 19), align 1 %strcmp.cmp75 = icmp ne i8 %41, %42 br i1 %strcmp.cmp75, label %strcmp.false, label %strcmp.loop_null_cmp74 strcmp.loop_null_cmp70: ; preds = %strcmp.loop65 %strcmp.cmp_null72 = icmp eq i8 %39, 0 br i1 %strcmp.cmp_null72, label %strcmp.done, label %strcmp.loop69 strcmp.loop73: ; preds = %strcmp.loop_null_cmp74 %43 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 20), align 1 %44 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 20), align 1 %strcmp.cmp79 = icmp ne i8 %43, %44 br i1 %strcmp.cmp79, label %strcmp.false, label %strcmp.loop_null_cmp78 strcmp.loop_null_cmp74: ; preds = %strcmp.loop69 %strcmp.cmp_null76 = icmp eq i8 %41, 0 br i1 %strcmp.cmp_null76, label %strcmp.done, label %strcmp.loop73 strcmp.loop77: ; preds = %strcmp.loop_null_cmp78 %45 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 21), align 1 %46 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 21), align 1 %strcmp.cmp83 = icmp ne i8 %45, %46 br i1 %strcmp.cmp83, label %strcmp.false, label %strcmp.loop_null_cmp82 strcmp.loop_null_cmp78: ; preds = %strcmp.loop73 %strcmp.cmp_null80 = icmp eq i8 %43, 0 br i1 %strcmp.cmp_null80, label %strcmp.done, label %strcmp.loop77 strcmp.loop81: ; preds = %strcmp.loop_null_cmp82 %47 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 22), align 1 %48 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 22), align 1 %strcmp.cmp87 = icmp ne i8 %47, %48 br i1 %strcmp.cmp87, label %strcmp.false, label %strcmp.loop_null_cmp86 strcmp.loop_null_cmp82: ; preds = %strcmp.loop77 %strcmp.cmp_null84 = icmp eq i8 %45, 0 br i1 %strcmp.cmp_null84, label %strcmp.done, label %strcmp.loop81 strcmp.loop85: ; preds = %strcmp.loop_null_cmp86 %49 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 23), align 1 %50 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 23), align 1 %strcmp.cmp91 = icmp ne i8 %49, %50 br i1 %strcmp.cmp91, label %strcmp.false, label %strcmp.loop_null_cmp90 strcmp.loop_null_cmp86: ; preds = %strcmp.loop81 %strcmp.cmp_null88 = icmp eq i8 %47, 0 br i1 %strcmp.cmp_null88, label %strcmp.done, label %strcmp.loop85 strcmp.loop89: ; preds = %strcmp.loop_null_cmp90 %51 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 24), align 1 %52 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 24), align 1 %strcmp.cmp95 = icmp ne i8 %51, %52 br i1 %strcmp.cmp95, label %strcmp.false, label %strcmp.loop_null_cmp94 strcmp.loop_null_cmp90: ; preds = %strcmp.loop85 %strcmp.cmp_null92 = icmp eq i8 %49, 0 br i1 %strcmp.cmp_null92, label %strcmp.done, label %strcmp.loop89 strcmp.loop93: ; preds = %strcmp.loop_null_cmp94 %53 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 25), align 1 %54 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 25), align 1 %strcmp.cmp99 = icmp ne i8 %53, %54 br i1 %strcmp.cmp99, label %strcmp.false, label %strcmp.loop_null_cmp98 strcmp.loop_null_cmp94: ; preds = %strcmp.loop89 %strcmp.cmp_null96 = icmp eq i8 %51, 0 br i1 %strcmp.cmp_null96, label %strcmp.done, label %strcmp.loop93 strcmp.loop97: ; preds = %strcmp.loop_null_cmp98 %55 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 26), align 1 %56 = load i8, ptr getelementptr (i8, ptr @"tracepoint:sched:sched_one", i32 26), align 1 %strcmp.cmp103 = icmp ne i8 %55, %56 br i1 %strcmp.cmp103, label %strcmp.false, label %strcmp.loop_null_cmp102 strcmp.loop_null_cmp98: ; preds = %strcmp.loop93 %strcmp.cmp_null100 = icmp eq i8 %53, 0 br i1 %strcmp.cmp_null100, label %strcmp.done, label %strcmp.loop97 strcmp.loop101: ; preds = %strcmp.loop_null_cmp102 br label %strcmp.done strcmp.loop_null_cmp102: ; preds = %strcmp.loop97 %strcmp.cmp_null104 = icmp eq i8 %55, 0 br i1 %strcmp.cmp_null104, label %strcmp.done, label %strcmp.loop101 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "tracepoint_sched_sched_one_1", linkageName: "tracepoint_sched_sched_one_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/builtin_probe_wild.ll000066400000000000000000000133371477746507000233410ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !33 @"tracepoint:sched:sched_one" = global [27 x i8] c"tracepoint:sched:sched_one\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_sched_sched_one_1(ptr %0) section "s_tracepoint_sched_sched_one_1" !dbg !49 { entry: %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr @"tracepoint:sched:sched_one", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 1 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 216, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 27, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !28} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !19) !28 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !29, size: 64, offset: 64) !29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64) !30 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !31) !31 = !{!32} !32 = !DISubrange(count: 262144, lowerBound: 0) !33 = !DIGlobalVariableExpression(var: !34, expr: !DIExpression()) !34 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !35 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !36) !36 = !{!37, !11, !42, !45} !37 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !38, size: 64) !38 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !39, size: 64) !39 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !40) !40 = !{!41} !41 = !DISubrange(count: 2, lowerBound: 0) !42 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !43, size: 64, offset: 128) !43 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !44, size: 64) !44 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !45 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !21, !33} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "tracepoint_sched_sched_one_1", linkageName: "tracepoint_sched_sched_one_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!14, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/builtin_rand.ll000066400000000000000000000130611477746507000221310ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_random = call i32 inttoptr (i64 7 to ptr)() %1 = zext i32 %get_random to i64 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_retval.ll000066400000000000000000000135531477746507000225100ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kretprobe_f_1(ptr %0) section "s_kretprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 10 %retval = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %retval, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kretprobe_f_1", linkageName: "kretprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/builtin_sarg.ll000066400000000000000000000165401477746507000221460ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %sarg2 = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %sarg0 = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 19 %reg_sp = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %sarg0) %3 = add i64 %reg_sp, 8 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %sarg0, i32 8, i64 %3) %4 = load i64, ptr %sarg0, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %sarg0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %4, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %5 = call ptr @llvm.preserve.static.offset(ptr %0) %6 = getelementptr i64, ptr %5, i64 19 %reg_sp1 = load volatile i64, ptr %6, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %sarg2) %7 = add i64 %reg_sp1, 24 %probe_read_kernel2 = call i64 inttoptr (i64 113 to ptr)(ptr %sarg2, i32 8, i64 %7) %8 = load i64, ptr %sarg2, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %sarg2) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") store i64 %8, ptr %"@y_val", align 8 %update_elem3 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/builtin_uid_gid.ll000066400000000000000000000147031477746507000226150ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_uid_gid = call i64 inttoptr (i64 15 to ptr)() %1 = and i64 %get_uid_gid, 4294967295 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %get_uid_gid1 = call i64 inttoptr (i64 15 to ptr)() %2 = lshr i64 %get_uid_gid1, 32 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") store i64 %2, ptr %"@y_val", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/builtin_username.ll000066400000000000000000000147031477746507000230300ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_uid_gid = call i64 inttoptr (i64 15 to ptr)() %1 = and i64 %get_uid_gid, 4294967295 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %get_uid_gid1 = call i64 inttoptr (i64 15 to ptr)() %2 = lshr i64 %get_uid_gid1, 32 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") store i64 %2, ptr %"@y_val", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/builtin_ustack.ll000066400000000000000000000332441477746507000225040ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct map_t.2" = type { ptr, ptr } %"struct map_t.3" = type { ptr, ptr, ptr, ptr } %ustack_key = type { i64, i64, i32, i32 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @stack_bpftrace_127 = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @stack_scratch = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !45 @ringbuf = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !57 @event_loss_counter = dso_local global %"struct map_t.3" zeroinitializer, section ".maps", !dbg !71 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !84 { entry: %"@x_key" = alloca i64, align 8 %lookup_stack_scratch_key = alloca i32, align 4 %stack_key = alloca %ustack_key, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key) call void @llvm.memset.p0.i64(ptr align 1 %stack_key, i8 0, i64 24, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key) store i32 0, ptr %lookup_stack_scratch_key, align 4 %lookup_stack_scratch_map = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key) %lookup_stack_scratch_cond = icmp ne ptr %lookup_stack_scratch_map, null br i1 %lookup_stack_scratch_cond, label %lookup_stack_scratch_merge, label %lookup_stack_scratch_failure stack_scratch_failure: ; preds = %lookup_stack_scratch_failure br label %merge_block merge_block: ; preds = %stack_scratch_failure, %get_stack_success, %get_stack_fail %1 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 2 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %2 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %2 to i32 store i32 %pid, ptr %1, align 4 %3 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 3 store i32 0, ptr %3, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %stack_key, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 lookup_stack_scratch_failure: ; preds = %entry br label %stack_scratch_failure lookup_stack_scratch_merge: ; preds = %entry %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map, i32 1016, ptr null) %get_stack = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map, i32 1016, i64 256) %4 = icmp sge i64 %get_stack, 0 br i1 %4, label %get_stack_success, label %get_stack_fail get_stack_success: ; preds = %lookup_stack_scratch_merge %5 = udiv i64 %get_stack, 8 %6 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 1 store i64 %5, ptr %6, align 8 %7 = trunc i64 %5 to i8 %murmur_hash_2 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map, i8 %7, i64 1) %8 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 0 store i64 %murmur_hash_2, ptr %8, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_127, ptr %stack_key, ptr %lookup_stack_scratch_map, i64 0) br label %merge_block get_stack_fail: ; preds = %lookup_stack_scratch_merge br label %merge_block } ; Function Attrs: alwaysinline define internal i64 @murmur_hash_2(ptr %0, i8 %1, i64 %2) #1 section "helpers" { entry: %k = alloca i64, align 8 %i = alloca i8, align 1 %id = alloca i64, align 8 %seed_addr = alloca i64, align 8 %nr_stack_frames_addr = alloca i8, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %id) call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %k) store i8 %1, ptr %nr_stack_frames_addr, align 1 store i64 %2, ptr %seed_addr, align 8 %3 = load i8, ptr %nr_stack_frames_addr, align 1 %4 = zext i8 %3 to i64 %5 = mul i64 %4, -4132994306676758123 %6 = load i64, ptr %seed_addr, align 8 %7 = xor i64 %6, %5 store i64 %7, ptr %id, align 8 store i8 0, ptr %i, align 1 br label %while_cond while_cond: ; preds = %while_body, %entry %8 = load i8, ptr %nr_stack_frames_addr, align 1 %9 = load i8, ptr %i, align 1 %length.cmp = icmp ult i8 %9, %8 br i1 %length.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %10 = load i8, ptr %i, align 1 %11 = getelementptr i64, ptr %0, i8 %10 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %k, align 8 %13 = load i64, ptr %k, align 8 %14 = mul i64 %13, -4132994306676758123 store i64 %14, ptr %k, align 8 %15 = load i64, ptr %k, align 8 %16 = lshr i64 %15, 47 %17 = load i64, ptr %k, align 8 %18 = xor i64 %17, %16 store i64 %18, ptr %k, align 8 %19 = load i64, ptr %k, align 8 %20 = mul i64 %19, -4132994306676758123 store i64 %20, ptr %k, align 8 %21 = load i64, ptr %k, align 8 %22 = load i64, ptr %id, align 8 %23 = xor i64 %22, %21 store i64 %23, ptr %id, align 8 %24 = load i64, ptr %id, align 8 %25 = mul i64 %24, -4132994306676758123 store i64 %25, ptr %id, align 8 %26 = load i8, ptr %i, align 1 %27 = add i8 %26, 1 store i8 %27, ptr %i, align 1 br label %while_cond while_end: ; preds = %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %i) call void @llvm.lifetime.end.p0(i64 -1, ptr %k) %28 = load i64, ptr %id, align 8 %zero_cond = icmp eq i64 %28, 0 br i1 %zero_cond, label %if_zero, label %if_end if_zero: ; preds = %while_end store i64 1, ptr %id, align 8 br label %if_end if_end: ; preds = %if_zero, %while_end %29 = load i64, ptr %id, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %id) ret i64 %29 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!81} !llvm.module.flags = !{!83} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 192, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 24, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "stack_bpftrace_127", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !24) !24 = !{!25, !30, !35, !40} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 288, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 9, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 4194304, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 131072, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !36, size: 64, offset: 128) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 96, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 12, lowerBound: 0) !40 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !41, size: 64, offset: 192) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 8128, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 127, lowerBound: 0) !45 = !DIGlobalVariableExpression(var: !46, expr: !DIExpression()) !46 = distinct !DIGlobalVariable(name: "stack_scratch", linkageName: "global", scope: !2, file: !2, type: !47, isLocal: false, isDefinition: true) !47 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !48) !48 = !{!49, !11, !54, !40} !49 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !50, size: 64) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !52) !52 = !{!53} !53 = !DISubrange(count: 6, lowerBound: 0) !54 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !55, size: 64, offset: 128) !55 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !56, size: 64) !56 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !57 = !DIGlobalVariableExpression(var: !58, expr: !DIExpression()) !58 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !59, isLocal: false, isDefinition: true) !59 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !60) !60 = !{!61, !66} !61 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !62, size: 64) !62 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !63, size: 64) !63 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !64) !64 = !{!65} !65 = !DISubrange(count: 27, lowerBound: 0) !66 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !67, size: 64, offset: 64) !67 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !68, size: 64) !68 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !69) !69 = !{!70} !70 = !DISubrange(count: 262144, lowerBound: 0) !71 = !DIGlobalVariableExpression(var: !72, expr: !DIExpression()) !72 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !73, isLocal: false, isDefinition: true) !73 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !74) !74 = !{!75, !11, !54, !80} !75 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !76, size: 64) !76 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !77, size: 64) !77 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !78) !78 = !{!79} !79 = !DISubrange(count: 2, lowerBound: 0) !80 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !81 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !82) !82 = !{!0, !21, !45, !57, !71} !83 = !{i32 2, !"Debug Info Version", i32 3} !84 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !85, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !81, retainedNodes: !88) !85 = !DISubroutineType(types: !86) !86 = !{!14, !87} !87 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !88 = !{!89} !89 = !DILocalVariable(name: "ctx", arg: 1, scope: !84, file: !2, type: !87) bpftrace-0.23.2/tests/codegen/llvm/call_avg.ll000066400000000000000000000173201477746507000212310ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %avg_stas_val = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !52 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %avg_struct = alloca %avg_stas_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %3 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 0 %4 = load i64, ptr %3, align 8 %5 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 1 %6 = load i64, ptr %5, align 8 %7 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 0 %8 = add i64 %4, %2 store i64 %8, ptr %7, align 8 %9 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 1 %10 = add i64 1, %6 store i64 %10, ptr %9, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %avg_struct) %11 = getelementptr %avg_stas_val, ptr %avg_struct, i64 0, i32 0 store i64 %2, ptr %11, align 8 %12 = getelementptr %avg_stas_val, ptr %avg_struct, i64 0, i32 1 store i64 1, ptr %12, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %avg_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %avg_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !51} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !51 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !52 = !DIGlobalVariableExpression(var: !53, expr: !DIExpression()) !53 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !26, !40, !52} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !62) !58 = !DISubroutineType(types: !59) !59 = !{!18, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !61, size: 64) !61 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !62 = !{!63} !63 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/call_buf_implicit_size.ll000066400000000000000000000176731477746507000241670ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %buffer_16_t = type <{ i32, [16 x i8] }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !48 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !50 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !60 { entry: %"@x_key" = alloca i64, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 store i64 0, ptr %"$foo", align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %3 = getelementptr %buffer_16_t, ptr %2, i32 0, i32 0 store i32 16, ptr %3, align 4 %4 = getelementptr %buffer_16_t, ptr %2, i32 0, i32 1 call void @llvm.memset.p0.i64(ptr align 1 %4, i8 0, i64 16, i1 false) %5 = load i64, ptr %"$foo", align 8 %6 = inttoptr i64 %5 to ptr %7 = call ptr @llvm.preserve.static.offset(ptr %6) %8 = getelementptr i8, ptr %7, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %4, i32 16, ptr %8) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %2, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!57} !llvm.module.flags = !{!59} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 160, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 20, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = !DIGlobalVariableExpression(var: !49, expr: !DIExpression()) !49 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !50 = !DIGlobalVariableExpression(var: !51, expr: !DIExpression()) !51 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !52, isLocal: false, isDefinition: true) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !53, size: 8192, elements: !9) !53 = !DICompositeType(tag: DW_TAG_array_type, baseType: !54, size: 8192, elements: !9) !54 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 8192, elements: !55) !55 = !{!56} !56 = !DISubrange(count: 1024, lowerBound: 0) !57 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !58) !58 = !{!0, !21, !35, !48, !50} !59 = !{i32 2, !"Debug Info Version", i32 3} !60 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !57, retainedNodes: !64) !61 = !DISubroutineType(types: !62) !62 = !{!14, !63} !63 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !64 = !{!65} !65 = !DILocalVariable(name: "ctx", arg: 1, scope: !60, file: !2, type: !63) bpftrace-0.23.2/tests/codegen/llvm/call_buf_size_literal.ll000066400000000000000000000173771477746507000240120ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %buffer_1_t = type <{ i32, [1 x i8] }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !48 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !50 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !60 { entry: %"@x_key" = alloca i64, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %3 = getelementptr %buffer_1_t, ptr %2, i32 0, i32 0 store i32 1, ptr %3, align 4 %4 = getelementptr %buffer_1_t, ptr %2, i32 0, i32 1 call void @llvm.memset.p0.i64(ptr align 1 %4, i8 0, i64 1, i1 false) %5 = call ptr @llvm.preserve.static.offset(ptr %0) %6 = getelementptr i64, ptr %5, i64 14 %arg0 = load volatile i64, ptr %6, align 8 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %4, i32 1, i64 %arg0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %2, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #3 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!57} !llvm.module.flags = !{!59} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 40, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 5, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = !DIGlobalVariableExpression(var: !49, expr: !DIExpression()) !49 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !50 = !DIGlobalVariableExpression(var: !51, expr: !DIExpression()) !51 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !52, isLocal: false, isDefinition: true) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !53, size: 8192, elements: !9) !53 = !DICompositeType(tag: DW_TAG_array_type, baseType: !54, size: 8192, elements: !9) !54 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 8192, elements: !55) !55 = !{!56} !56 = !DISubrange(count: 1024, lowerBound: 0) !57 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !58) !58 = !{!0, !21, !35, !48, !50} !59 = !{i32 2, !"Debug Info Version", i32 3} !60 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !57, retainedNodes: !64) !61 = !DISubroutineType(types: !62) !62 = !{!14, !63} !63 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !64 = !{!65} !65 = !DILocalVariable(name: "ctx", arg: 1, scope: !60, file: !2, type: !63) bpftrace-0.23.2/tests/codegen/llvm/call_buf_size_nonliteral.ll000066400000000000000000000172531477746507000245160ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %buffer_1020_t = type <{ i32, [1020 x i8] }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !48 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !50 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %"@x_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 13 %arg1 = load volatile i64, ptr %2, align 8 %length.cmp = icmp ule i64 %arg1, 1020 %length.select = select i1 %length.cmp, i64 %arg1, i64 1020 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %3 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %3 %4 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %5 = getelementptr %buffer_1020_t, ptr %4, i32 0, i32 0 %6 = trunc i64 %length.select to i32 store i32 %6, ptr %5, align 4 %7 = getelementptr %buffer_1020_t, ptr %4, i32 0, i32 1 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %7, i32 1020, ptr null) %8 = call ptr @llvm.preserve.static.offset(ptr %0) %9 = getelementptr i64, ptr %8, i64 14 %arg0 = load volatile i64, ptr %9, align 8 %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %7, i32 %6, i64 %arg0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %4, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 8192, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 1024, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = !DIGlobalVariableExpression(var: !49, expr: !DIExpression()) !49 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !50 = !DIGlobalVariableExpression(var: !51, expr: !DIExpression()) !51 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !52, isLocal: false, isDefinition: true) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !53, size: 8192, elements: !9) !53 = !DICompositeType(tag: DW_TAG_array_type, baseType: !17, size: 8192, elements: !9) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !21, !35, !48, !50} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !61) !58 = !DISubroutineType(types: !59) !59 = !{!14, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/call_cat.ll000066400000000000000000000140361477746507000212240ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %cat_t = type { i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %cat_args = alloca %cat_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %cat_args) call void @llvm.memset.p0.i64(ptr align 1 %cat_args, i8 0, i64 8, i1 false) %1 = getelementptr %cat_t, ptr %cat_args, i32 0, i32 0 store i64 20000, ptr %1, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %cat_args, i64 8, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %cat_args) ret i64 0 lookup_success: ; preds = %event_loss_counter %2 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_cgroup.ll000066400000000000000000000137711477746507000217610ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_syscalls_sys_enter_openat_1(ptr %0) section "s_tracepoint_syscalls_sys_enter_openat_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_cgroup_id = call i64 inttoptr (i64 80 to ptr)() %1 = icmp eq i64 %get_cgroup_id, 4294967297 %2 = zext i1 %1 to i64 %predcond = icmp eq i64 %2, 0 br i1 %predcond, label %pred_false, label %pred_true pred_false: ; preds = %entry ret i64 1 pred_true: ; preds = %entry %get_cgroup_id1 = call i64 inttoptr (i64 80 to ptr)() call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_cgroup_id1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 1 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "tracepoint_syscalls_sys_enter_openat_1", linkageName: "tracepoint_syscalls_sys_enter_openat_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_cgroup_path.ll000066400000000000000000000164141477746507000227720ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %print_cgroup_path_t_16_t = type <{ i64, i64, [16 x i8] }> %cgroup_path_t = type <{ i64, i64 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %print_cgroup_path_t_16_t = alloca %print_cgroup_path_t_16_t, align 8 %cgroup_path_args = alloca %cgroup_path_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %cgroup_path_args) %1 = getelementptr %cgroup_path_t, ptr %cgroup_path_args, i64 0, i32 0 store i64 0, ptr %1, align 8 %get_cgroup_id = call i64 inttoptr (i64 80 to ptr)() %2 = getelementptr %cgroup_path_t, ptr %cgroup_path_args, i64 0, i32 1 store i64 %get_cgroup_id, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %print_cgroup_path_t_16_t) %3 = getelementptr %print_cgroup_path_t_16_t, ptr %print_cgroup_path_t_16_t, i64 0, i32 0 store i64 30007, ptr %3, align 8 %4 = getelementptr %print_cgroup_path_t_16_t, ptr %print_cgroup_path_t_16_t, i64 0, i32 1 store i64 0, ptr %4, align 8 %5 = getelementptr %print_cgroup_path_t_16_t, ptr %print_cgroup_path_t_16_t, i32 0, i32 2 call void @llvm.memset.p0.i64(ptr align 1 %5, i8 0, i64 16, i1 false) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %5, ptr align 1 %cgroup_path_args, i64 16, i1 false) %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %print_cgroup_path_t_16_t, i64 32, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %print_cgroup_path_t_16_t) call void @llvm.lifetime.end.p0(i64 -1, ptr %cgroup_path_args) ret i64 0 lookup_success: ; preds = %event_loss_counter %6 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_clear.ll000066400000000000000000000164631477746507000215510ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %clear_t = type <{ i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define i64 @kprobe_f_2(ptr %0) section "s_kprobe_f_2" !dbg !52 { entry: %key = alloca i32, align 4 %"clear_@x" = alloca %clear_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"clear_@x") %1 = getelementptr %clear_t, ptr %"clear_@x", i64 0, i32 0 store i64 30002, ptr %1, align 8 %2 = getelementptr %clear_t, ptr %"clear_@x", i64 0, i32 1 store i32 0, ptr %2, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %"clear_@x", i64 12, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %"clear_@x") ret i64 0 lookup_success: ; preds = %event_loss_counter %3 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) !52 = distinct !DISubprogram(name: "kprobe_f_2", linkageName: "kprobe_f_2", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !53) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !52, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_count.ll000066400000000000000000000152131477746507000216030ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !22 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !36 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !45 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 %2 = add i64 %1, 1 store i64 %2, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 6, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !22 = !DIGlobalVariableExpression(var: !23, expr: !DIExpression()) !23 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !24 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !25) !25 = !{!26, !31} !26 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !27, size: 64) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !29) !29 = !{!30} !30 = !DISubrange(count: 27, lowerBound: 0) !31 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !32, size: 64, offset: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 262144, lowerBound: 0) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !38, isLocal: false, isDefinition: true) !38 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !39) !39 = !{!40, !11, !16, !19} !40 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !41, size: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 2, lowerBound: 0) !45 = !DIGlobalVariableExpression(var: !46, expr: !DIExpression()) !46 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !21, isLocal: false, isDefinition: true) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !22, !36, !45} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!21, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/call_delete.ll000066400000000000000000000140741477746507000217210ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 1, ptr %"@x_key1", align 8 %delete_elem = call i64 inttoptr (i64 3 to ptr)(ptr @AT_x, ptr %"@x_key1") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/call_delete_deprecated.ll000066400000000000000000000140741477746507000241010ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 1, ptr %"@x_key1", align 8 %delete_elem = call i64 inttoptr (i64 3 to ptr)(ptr @AT_x, ptr %"@x_key1") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/call_exit.ll000066400000000000000000000157501477746507000214320ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %exit_t = type <{ i64, i8 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %key = alloca i32, align 4 %exit = alloca %exit_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %exit) %1 = getelementptr %exit_t, ptr %exit, i64 0, i32 0 store i64 30000, ptr %1, align 8 %2 = getelementptr %exit_t, ptr %exit, i64 0, i32 1 store i8 0, ptr %2, align 1 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %exit, i64 9, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %exit) ret i64 0 lookup_success: ; preds = %event_loss_counter %3 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge deadcode: ; No predecessors! call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 10, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_exit_with_error_code.ll000066400000000000000000000135601477746507000246650ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %exit_t = type <{ i64, i8 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %exit = alloca %exit_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %exit) %1 = getelementptr %exit_t, ptr %exit, i64 0, i32 0 store i64 30000, ptr %1, align 8 %2 = getelementptr %exit_t, ptr %exit, i64 0, i32 1 store i64 1, ptr %2, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %exit, i64 9, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %exit) ret i64 0 lookup_success: ; preds = %event_loss_counter %3 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge deadcode: ; No predecessors! ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_has_key.ll000066400000000000000000000141501477746507000220750ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 1, ptr %"@x_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key1") %has_key = icmp ne ptr %lookup_elem, null call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/call_hist.ll000066400000000000000000000216661477746507000214330ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !54 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %log2 = call i64 @log2(i64 %2, i64 0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 %log2, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %3 = load i64, ptr %lookup_elem, align 8 %4 = add i64 %3, 1 store i64 %4, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: alwaysinline define internal i64 @log2(i64 %0, i64 %1) #1 section "helpers" { entry: %2 = alloca i64, align 8 %3 = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %3) store i64 %0, ptr %3, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %2) store i64 %1, ptr %2, align 8 %4 = load i64, ptr %3, align 8 %5 = icmp slt i64 %4, 0 br i1 %5, label %hist.is_less_than_zero, label %hist.is_not_less_than_zero hist.is_less_than_zero: ; preds = %entry ret i64 0 hist.is_not_less_than_zero: ; preds = %entry %6 = load i64, ptr %2, align 8 %7 = shl i64 1, %6 %8 = sub i64 %7, 1 %9 = icmp ule i64 %4, %8 br i1 %9, label %hist.is_zero, label %hist.is_not_zero hist.is_zero: ; preds = %hist.is_not_less_than_zero %10 = add i64 %4, 1 ret i64 %10 hist.is_not_zero: ; preds = %hist.is_not_less_than_zero %11 = icmp sge i64 %4, 4294967296 %12 = zext i1 %11 to i64 %13 = shl i64 %12, 5 %14 = lshr i64 %4, %13 %15 = add i64 0, %13 %16 = icmp sge i64 %14, 65536 %17 = zext i1 %16 to i64 %18 = shl i64 %17, 4 %19 = lshr i64 %14, %18 %20 = add i64 %15, %18 %21 = icmp sge i64 %19, 256 %22 = zext i1 %21 to i64 %23 = shl i64 %22, 3 %24 = lshr i64 %19, %23 %25 = add i64 %20, %23 %26 = icmp sge i64 %24, 16 %27 = zext i1 %26 to i64 %28 = shl i64 %27, 2 %29 = lshr i64 %24, %28 %30 = add i64 %25, %28 %31 = icmp sge i64 %29, 4 %32 = zext i1 %31 to i64 %33 = shl i64 %32, 1 %34 = lshr i64 %29, %33 %35 = add i64 %30, %33 %36 = icmp sge i64 %34, 2 %37 = zext i1 %36 to i64 %38 = shl i64 %37, 0 %39 = lshr i64 %34, %38 %40 = add i64 %35, %38 %41 = sub i64 %40, %6 %42 = load i64, ptr %3, align 8 %43 = lshr i64 %42, %41 %44 = and i64 %43, %8 %45 = add i64 %41, 1 %46 = shl i64 %45, %6 %47 = add i64 %46, %44 %48 = add i64 %47, 1 ret i64 %48 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!51} !llvm.module.flags = !{!53} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !48, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !44, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 1, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !49, size: 64, offset: 128) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !51 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !52) !52 = !{!0, !20, !34} !53 = !{i32 2, !"Debug Info Version", i32 3} !54 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !55, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !51, retainedNodes: !59) !55 = !DISubroutineType(types: !56) !56 = !{!18, !57} !57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !58, size: 64) !58 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !54, file: !2, type: !57) bpftrace-0.23.2/tests/codegen/llvm/call_join.ll000066400000000000000000000320621477746507000214130ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @join = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !54 { entry: %key = alloca i32, align 4 %join_r0 = alloca i64, align 8 %lookup_join_key = alloca i32, align 4 %"struct arg.argv" = alloca i64, align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 store i64 0, ptr %"$x", align 8 %1 = load i64, ptr %"$x", align 8 %2 = inttoptr i64 %1 to ptr %3 = call ptr @llvm.preserve.static.offset(ptr %2) %4 = getelementptr i8, ptr %3, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct arg.argv") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct arg.argv", i32 8, ptr %4) %5 = load i64, ptr %"struct arg.argv", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct arg.argv") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_join_key) store i32 0, ptr %lookup_join_key, align 4 %lookup_join_map = call ptr inttoptr (i64 1 to ptr)(ptr @join, ptr %lookup_join_key) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_join_key) %lookup_join_cond = icmp ne ptr %lookup_join_map, null br i1 %lookup_join_cond, label %lookup_join_merge, label %lookup_join_failure failure_callback: ; preds = %counter_merge, %lookup_join_failure ret i64 0 lookup_join_failure: ; preds = %entry br label %failure_callback lookup_join_merge: ; preds = %entry store i64 30005, ptr %lookup_join_map, align 8 %6 = getelementptr i8, ptr %lookup_join_map, i64 8 store i64 0, ptr %6, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %join_r0) %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %5) %7 = load i64, ptr %join_r0, align 8 %8 = getelementptr i8, ptr %lookup_join_map, i64 16 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %8, i32 1024, i64 %7) %9 = add i64 %5, 8 %probe_read_kernel2 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %9) %10 = load i64, ptr %join_r0, align 8 %11 = getelementptr i8, ptr %lookup_join_map, i64 1040 %probe_read_kernel_str3 = call i64 inttoptr (i64 115 to ptr)(ptr %11, i32 1024, i64 %10) %12 = add i64 %9, 8 %probe_read_kernel4 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %12) %13 = load i64, ptr %join_r0, align 8 %14 = getelementptr i8, ptr %lookup_join_map, i64 2064 %probe_read_kernel_str5 = call i64 inttoptr (i64 115 to ptr)(ptr %14, i32 1024, i64 %13) %15 = add i64 %12, 8 %probe_read_kernel6 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %15) %16 = load i64, ptr %join_r0, align 8 %17 = getelementptr i8, ptr %lookup_join_map, i64 3088 %probe_read_kernel_str7 = call i64 inttoptr (i64 115 to ptr)(ptr %17, i32 1024, i64 %16) %18 = add i64 %15, 8 %probe_read_kernel8 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %18) %19 = load i64, ptr %join_r0, align 8 %20 = getelementptr i8, ptr %lookup_join_map, i64 4112 %probe_read_kernel_str9 = call i64 inttoptr (i64 115 to ptr)(ptr %20, i32 1024, i64 %19) %21 = add i64 %18, 8 %probe_read_kernel10 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %21) %22 = load i64, ptr %join_r0, align 8 %23 = getelementptr i8, ptr %lookup_join_map, i64 5136 %probe_read_kernel_str11 = call i64 inttoptr (i64 115 to ptr)(ptr %23, i32 1024, i64 %22) %24 = add i64 %21, 8 %probe_read_kernel12 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %24) %25 = load i64, ptr %join_r0, align 8 %26 = getelementptr i8, ptr %lookup_join_map, i64 6160 %probe_read_kernel_str13 = call i64 inttoptr (i64 115 to ptr)(ptr %26, i32 1024, i64 %25) %27 = add i64 %24, 8 %probe_read_kernel14 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %27) %28 = load i64, ptr %join_r0, align 8 %29 = getelementptr i8, ptr %lookup_join_map, i64 7184 %probe_read_kernel_str15 = call i64 inttoptr (i64 115 to ptr)(ptr %29, i32 1024, i64 %28) %30 = add i64 %27, 8 %probe_read_kernel16 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %30) %31 = load i64, ptr %join_r0, align 8 %32 = getelementptr i8, ptr %lookup_join_map, i64 8208 %probe_read_kernel_str17 = call i64 inttoptr (i64 115 to ptr)(ptr %32, i32 1024, i64 %31) %33 = add i64 %30, 8 %probe_read_kernel18 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %33) %34 = load i64, ptr %join_r0, align 8 %35 = getelementptr i8, ptr %lookup_join_map, i64 9232 %probe_read_kernel_str19 = call i64 inttoptr (i64 115 to ptr)(ptr %35, i32 1024, i64 %34) %36 = add i64 %33, 8 %probe_read_kernel20 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %36) %37 = load i64, ptr %join_r0, align 8 %38 = getelementptr i8, ptr %lookup_join_map, i64 10256 %probe_read_kernel_str21 = call i64 inttoptr (i64 115 to ptr)(ptr %38, i32 1024, i64 %37) %39 = add i64 %36, 8 %probe_read_kernel22 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %39) %40 = load i64, ptr %join_r0, align 8 %41 = getelementptr i8, ptr %lookup_join_map, i64 11280 %probe_read_kernel_str23 = call i64 inttoptr (i64 115 to ptr)(ptr %41, i32 1024, i64 %40) %42 = add i64 %39, 8 %probe_read_kernel24 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %42) %43 = load i64, ptr %join_r0, align 8 %44 = getelementptr i8, ptr %lookup_join_map, i64 12304 %probe_read_kernel_str25 = call i64 inttoptr (i64 115 to ptr)(ptr %44, i32 1024, i64 %43) %45 = add i64 %42, 8 %probe_read_kernel26 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %45) %46 = load i64, ptr %join_r0, align 8 %47 = getelementptr i8, ptr %lookup_join_map, i64 13328 %probe_read_kernel_str27 = call i64 inttoptr (i64 115 to ptr)(ptr %47, i32 1024, i64 %46) %48 = add i64 %45, 8 %probe_read_kernel28 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %48) %49 = load i64, ptr %join_r0, align 8 %50 = getelementptr i8, ptr %lookup_join_map, i64 14352 %probe_read_kernel_str29 = call i64 inttoptr (i64 115 to ptr)(ptr %50, i32 1024, i64 %49) %51 = add i64 %48, 8 %probe_read_kernel30 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %51) %52 = load i64, ptr %join_r0, align 8 %53 = getelementptr i8, ptr %lookup_join_map, i64 15376 %probe_read_kernel_str31 = call i64 inttoptr (i64 115 to ptr)(ptr %53, i32 1024, i64 %52) %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %lookup_join_map, i64 16400, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %lookup_join_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %lookup_join_merge br label %failure_callback lookup_success: ; preds = %event_loss_counter %54 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!51} !llvm.module.flags = !{!53} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "join", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 6, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 131200, elements: !23) !22 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !23 = !{!24} !24 = !DISubrange(count: 16400, lowerBound: 0) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !11, !16, !48} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !49, size: 64, offset: 192) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !51 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !52) !52 = !{!0, !25, !39} !53 = !{i32 2, !"Debug Info Version", i32 3} !54 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !55, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !51, retainedNodes: !58) !55 = !DISubroutineType(types: !56) !56 = !{!50, !57} !57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !58 = !{!59} !59 = !DILocalVariable(name: "ctx", arg: 1, scope: !54, file: !2, type: !57) bpftrace-0.23.2/tests/codegen/llvm/call_join_with_debug.ll000066400000000000000000000332531477746507000236170ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @join = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !54 { entry: %key = alloca i32, align 4 %join_r0 = alloca i64, align 8 %fmt_str = alloca [70 x i8], align 1 %lookup_join_key = alloca i32, align 4 %"struct arg.argv" = alloca i64, align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 store i64 0, ptr %"$x", align 8 %1 = load i64, ptr %"$x", align 8 %2 = inttoptr i64 %1 to ptr %3 = call ptr @llvm.preserve.static.offset(ptr %2) %4 = getelementptr i8, ptr %3, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct arg.argv") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct arg.argv", i32 8, ptr %4) %5 = load i64, ptr %"struct arg.argv", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct arg.argv") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_join_key) store i32 0, ptr %lookup_join_key, align 4 %lookup_join_map = call ptr inttoptr (i64 1 to ptr)(ptr @join, ptr %lookup_join_key) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_join_key) %lookup_join_cond = icmp ne ptr %lookup_join_map, null br i1 %lookup_join_cond, label %lookup_join_merge, label %lookup_join_failure failure_callback: ; preds = %counter_merge, %lookup_join_failure ret i64 0 lookup_join_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %fmt_str) call void @llvm.memset.p0.i64(ptr align 1 %fmt_str, i8 0, i64 70, i1 false) store [70 x i8] c"[BPFTRACE_DEBUG_OUTPUT] unable to find the scratch map value for join\00", ptr %fmt_str, align 1 %trace_printk = call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr %fmt_str, i32 70) br label %failure_callback lookup_join_merge: ; preds = %entry store i64 30005, ptr %lookup_join_map, align 8 %6 = getelementptr i8, ptr %lookup_join_map, i64 8 store i64 0, ptr %6, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %join_r0) %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %5) %7 = load i64, ptr %join_r0, align 8 %8 = getelementptr i8, ptr %lookup_join_map, i64 16 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %8, i32 1024, i64 %7) %9 = add i64 %5, 8 %probe_read_kernel2 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %9) %10 = load i64, ptr %join_r0, align 8 %11 = getelementptr i8, ptr %lookup_join_map, i64 1040 %probe_read_kernel_str3 = call i64 inttoptr (i64 115 to ptr)(ptr %11, i32 1024, i64 %10) %12 = add i64 %9, 8 %probe_read_kernel4 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %12) %13 = load i64, ptr %join_r0, align 8 %14 = getelementptr i8, ptr %lookup_join_map, i64 2064 %probe_read_kernel_str5 = call i64 inttoptr (i64 115 to ptr)(ptr %14, i32 1024, i64 %13) %15 = add i64 %12, 8 %probe_read_kernel6 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %15) %16 = load i64, ptr %join_r0, align 8 %17 = getelementptr i8, ptr %lookup_join_map, i64 3088 %probe_read_kernel_str7 = call i64 inttoptr (i64 115 to ptr)(ptr %17, i32 1024, i64 %16) %18 = add i64 %15, 8 %probe_read_kernel8 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %18) %19 = load i64, ptr %join_r0, align 8 %20 = getelementptr i8, ptr %lookup_join_map, i64 4112 %probe_read_kernel_str9 = call i64 inttoptr (i64 115 to ptr)(ptr %20, i32 1024, i64 %19) %21 = add i64 %18, 8 %probe_read_kernel10 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %21) %22 = load i64, ptr %join_r0, align 8 %23 = getelementptr i8, ptr %lookup_join_map, i64 5136 %probe_read_kernel_str11 = call i64 inttoptr (i64 115 to ptr)(ptr %23, i32 1024, i64 %22) %24 = add i64 %21, 8 %probe_read_kernel12 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %24) %25 = load i64, ptr %join_r0, align 8 %26 = getelementptr i8, ptr %lookup_join_map, i64 6160 %probe_read_kernel_str13 = call i64 inttoptr (i64 115 to ptr)(ptr %26, i32 1024, i64 %25) %27 = add i64 %24, 8 %probe_read_kernel14 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %27) %28 = load i64, ptr %join_r0, align 8 %29 = getelementptr i8, ptr %lookup_join_map, i64 7184 %probe_read_kernel_str15 = call i64 inttoptr (i64 115 to ptr)(ptr %29, i32 1024, i64 %28) %30 = add i64 %27, 8 %probe_read_kernel16 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %30) %31 = load i64, ptr %join_r0, align 8 %32 = getelementptr i8, ptr %lookup_join_map, i64 8208 %probe_read_kernel_str17 = call i64 inttoptr (i64 115 to ptr)(ptr %32, i32 1024, i64 %31) %33 = add i64 %30, 8 %probe_read_kernel18 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %33) %34 = load i64, ptr %join_r0, align 8 %35 = getelementptr i8, ptr %lookup_join_map, i64 9232 %probe_read_kernel_str19 = call i64 inttoptr (i64 115 to ptr)(ptr %35, i32 1024, i64 %34) %36 = add i64 %33, 8 %probe_read_kernel20 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %36) %37 = load i64, ptr %join_r0, align 8 %38 = getelementptr i8, ptr %lookup_join_map, i64 10256 %probe_read_kernel_str21 = call i64 inttoptr (i64 115 to ptr)(ptr %38, i32 1024, i64 %37) %39 = add i64 %36, 8 %probe_read_kernel22 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %39) %40 = load i64, ptr %join_r0, align 8 %41 = getelementptr i8, ptr %lookup_join_map, i64 11280 %probe_read_kernel_str23 = call i64 inttoptr (i64 115 to ptr)(ptr %41, i32 1024, i64 %40) %42 = add i64 %39, 8 %probe_read_kernel24 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %42) %43 = load i64, ptr %join_r0, align 8 %44 = getelementptr i8, ptr %lookup_join_map, i64 12304 %probe_read_kernel_str25 = call i64 inttoptr (i64 115 to ptr)(ptr %44, i32 1024, i64 %43) %45 = add i64 %42, 8 %probe_read_kernel26 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %45) %46 = load i64, ptr %join_r0, align 8 %47 = getelementptr i8, ptr %lookup_join_map, i64 13328 %probe_read_kernel_str27 = call i64 inttoptr (i64 115 to ptr)(ptr %47, i32 1024, i64 %46) %48 = add i64 %45, 8 %probe_read_kernel28 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %48) %49 = load i64, ptr %join_r0, align 8 %50 = getelementptr i8, ptr %lookup_join_map, i64 14352 %probe_read_kernel_str29 = call i64 inttoptr (i64 115 to ptr)(ptr %50, i32 1024, i64 %49) %51 = add i64 %48, 8 %probe_read_kernel30 = call i64 inttoptr (i64 113 to ptr)(ptr %join_r0, i32 8, i64 %51) %52 = load i64, ptr %join_r0, align 8 %53 = getelementptr i8, ptr %lookup_join_map, i64 15376 %probe_read_kernel_str31 = call i64 inttoptr (i64 115 to ptr)(ptr %53, i32 1024, i64 %52) %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %lookup_join_map, i64 16400, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %lookup_join_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %lookup_join_merge br label %failure_callback lookup_success: ; preds = %event_loss_counter %54 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!51} !llvm.module.flags = !{!53} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "join", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 6, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 131200, elements: !23) !22 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !23 = !{!24} !24 = !DISubrange(count: 16400, lowerBound: 0) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !11, !16, !48} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !49, size: 64, offset: 192) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !51 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !52) !52 = !{!0, !25, !39} !53 = !{i32 2, !"Debug Info Version", i32 3} !54 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !55, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !51, retainedNodes: !58) !55 = !DISubroutineType(types: !56) !56 = !{!50, !57} !57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !58 = !{!59} !59 = !DILocalVariable(name: "ctx", arg: 1, scope: !54, file: !2, type: !57) bpftrace-0.23.2/tests/codegen/llvm/call_kstack.ll000066400000000000000000000500411477746507000217310ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %"struct map_t.3" = type { ptr, ptr, ptr, ptr } %"struct map_t.4" = type { ptr, ptr, ptr, ptr } %"struct map_t.5" = type { ptr, ptr, ptr, ptr } %"struct map_t.6" = type { ptr, ptr } %"struct map_t.7" = type { ptr, ptr, ptr, ptr } %kstack_key = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @AT_z = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !23 @stack_perf_127 = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !25 @stack_bpftrace_6 = dso_local global %"struct map_t.3" zeroinitializer, section ".maps", !dbg !49 @stack_bpftrace_127 = dso_local global %"struct map_t.4" zeroinitializer, section ".maps", !dbg !58 @stack_scratch = dso_local global %"struct map_t.5" zeroinitializer, section ".maps", !dbg !60 @ringbuf = dso_local global %"struct map_t.6" zeroinitializer, section ".maps", !dbg !70 @event_loss_counter = dso_local global %"struct map_t.7" zeroinitializer, section ".maps", !dbg !84 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !97 { entry: %"@z_key" = alloca i64, align 8 %lookup_stack_scratch_key19 = alloca i32, align 4 %stack_key16 = alloca %kstack_key, align 8 %"@y_key" = alloca i64, align 8 %lookup_stack_scratch_key5 = alloca i32, align 4 %stack_key2 = alloca %kstack_key, align 8 %"@x_key" = alloca i64, align 8 %lookup_stack_scratch_key = alloca i32, align 4 %stack_key = alloca %kstack_key, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key) call void @llvm.memset.p0.i64(ptr align 1 %stack_key, i8 0, i64 16, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key) store i32 0, ptr %lookup_stack_scratch_key, align 4 %lookup_stack_scratch_map = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key) %lookup_stack_scratch_cond = icmp ne ptr %lookup_stack_scratch_map, null br i1 %lookup_stack_scratch_cond, label %lookup_stack_scratch_merge, label %lookup_stack_scratch_failure stack_scratch_failure: ; preds = %lookup_stack_scratch_failure br label %merge_block merge_block: ; preds = %stack_scratch_failure, %get_stack_success, %get_stack_fail call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %stack_key, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key2) call void @llvm.memset.p0.i64(ptr align 1 %stack_key2, i8 0, i64 16, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key5) store i32 0, ptr %lookup_stack_scratch_key5, align 4 %lookup_stack_scratch_map6 = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key5) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key5) %lookup_stack_scratch_cond9 = icmp ne ptr %lookup_stack_scratch_map6, null br i1 %lookup_stack_scratch_cond9, label %lookup_stack_scratch_merge8, label %lookup_stack_scratch_failure7 lookup_stack_scratch_failure: ; preds = %entry br label %stack_scratch_failure lookup_stack_scratch_merge: ; preds = %entry %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map, i32 1016, ptr null) %get_stack = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map, i32 1016, i64 0) %1 = icmp sge i64 %get_stack, 0 br i1 %1, label %get_stack_success, label %get_stack_fail get_stack_success: ; preds = %lookup_stack_scratch_merge %2 = udiv i64 %get_stack, 8 %3 = getelementptr %kstack_key, ptr %stack_key, i64 0, i32 1 store i64 %2, ptr %3, align 8 %4 = trunc i64 %2 to i8 %murmur_hash_2 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map, i8 %4, i64 1) %5 = getelementptr %kstack_key, ptr %stack_key, i64 0, i32 0 store i64 %murmur_hash_2, ptr %5, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_127, ptr %stack_key, ptr %lookup_stack_scratch_map, i64 0) br label %merge_block get_stack_fail: ; preds = %lookup_stack_scratch_merge br label %merge_block stack_scratch_failure3: ; preds = %lookup_stack_scratch_failure7 br label %merge_block4 merge_block4: ; preds = %stack_scratch_failure3, %get_stack_success10, %get_stack_fail11 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 %update_elem15 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %stack_key2, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key16) call void @llvm.memset.p0.i64(ptr align 1 %stack_key16, i8 0, i64 16, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key19) store i32 0, ptr %lookup_stack_scratch_key19, align 4 %lookup_stack_scratch_map20 = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key19) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key19) %lookup_stack_scratch_cond23 = icmp ne ptr %lookup_stack_scratch_map20, null br i1 %lookup_stack_scratch_cond23, label %lookup_stack_scratch_merge22, label %lookup_stack_scratch_failure21 lookup_stack_scratch_failure7: ; preds = %merge_block br label %stack_scratch_failure3 lookup_stack_scratch_merge8: ; preds = %merge_block call void @llvm.memset.p0.i64(ptr align 1 %lookup_stack_scratch_map6, i8 0, i64 48, i1 false) %get_stack12 = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map6, i32 48, i64 0) %6 = icmp sge i64 %get_stack12, 0 br i1 %6, label %get_stack_success10, label %get_stack_fail11 get_stack_success10: ; preds = %lookup_stack_scratch_merge8 %7 = udiv i64 %get_stack12, 8 %8 = getelementptr %kstack_key, ptr %stack_key2, i64 0, i32 1 store i64 %7, ptr %8, align 8 %9 = trunc i64 %7 to i8 %murmur_hash_213 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map6, i8 %9, i64 1) %10 = getelementptr %kstack_key, ptr %stack_key2, i64 0, i32 0 store i64 %murmur_hash_213, ptr %10, align 8 %update_elem14 = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_6, ptr %stack_key2, ptr %lookup_stack_scratch_map6, i64 0) br label %merge_block4 get_stack_fail11: ; preds = %lookup_stack_scratch_merge8 br label %merge_block4 stack_scratch_failure17: ; preds = %lookup_stack_scratch_failure21 br label %merge_block18 merge_block18: ; preds = %stack_scratch_failure17, %get_stack_success25, %get_stack_fail26 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@z_key") store i64 0, ptr %"@z_key", align 8 %update_elem30 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_z, ptr %"@z_key", ptr %stack_key16, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@z_key") ret i64 0 lookup_stack_scratch_failure21: ; preds = %merge_block4 br label %stack_scratch_failure17 lookup_stack_scratch_merge22: ; preds = %merge_block4 %probe_read_kernel24 = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map20, i32 1016, ptr null) %get_stack27 = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map20, i32 1016, i64 0) %11 = icmp sge i64 %get_stack27, 0 br i1 %11, label %get_stack_success25, label %get_stack_fail26 get_stack_success25: ; preds = %lookup_stack_scratch_merge22 %12 = udiv i64 %get_stack27, 8 %13 = getelementptr %kstack_key, ptr %stack_key16, i64 0, i32 1 store i64 %12, ptr %13, align 8 %14 = trunc i64 %12 to i8 %murmur_hash_228 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map20, i8 %14, i64 1) %15 = getelementptr %kstack_key, ptr %stack_key16, i64 0, i32 0 store i64 %murmur_hash_228, ptr %15, align 8 %update_elem29 = call i64 inttoptr (i64 2 to ptr)(ptr @stack_perf_127, ptr %stack_key16, ptr %lookup_stack_scratch_map20, i64 0) br label %merge_block18 get_stack_fail26: ; preds = %lookup_stack_scratch_merge22 br label %merge_block18 } ; Function Attrs: alwaysinline define internal i64 @murmur_hash_2(ptr %0, i8 %1, i64 %2) #1 section "helpers" { entry: %k = alloca i64, align 8 %i = alloca i8, align 1 %id = alloca i64, align 8 %seed_addr = alloca i64, align 8 %nr_stack_frames_addr = alloca i8, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %id) call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %k) store i8 %1, ptr %nr_stack_frames_addr, align 1 store i64 %2, ptr %seed_addr, align 8 %3 = load i8, ptr %nr_stack_frames_addr, align 1 %4 = zext i8 %3 to i64 %5 = mul i64 %4, -4132994306676758123 %6 = load i64, ptr %seed_addr, align 8 %7 = xor i64 %6, %5 store i64 %7, ptr %id, align 8 store i8 0, ptr %i, align 1 br label %while_cond while_cond: ; preds = %while_body, %entry %8 = load i8, ptr %nr_stack_frames_addr, align 1 %9 = load i8, ptr %i, align 1 %length.cmp = icmp ult i8 %9, %8 br i1 %length.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %10 = load i8, ptr %i, align 1 %11 = getelementptr i64, ptr %0, i8 %10 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %k, align 8 %13 = load i64, ptr %k, align 8 %14 = mul i64 %13, -4132994306676758123 store i64 %14, ptr %k, align 8 %15 = load i64, ptr %k, align 8 %16 = lshr i64 %15, 47 %17 = load i64, ptr %k, align 8 %18 = xor i64 %17, %16 store i64 %18, ptr %k, align 8 %19 = load i64, ptr %k, align 8 %20 = mul i64 %19, -4132994306676758123 store i64 %20, ptr %k, align 8 %21 = load i64, ptr %k, align 8 %22 = load i64, ptr %id, align 8 %23 = xor i64 %22, %21 store i64 %23, ptr %id, align 8 %24 = load i64, ptr %id, align 8 %25 = mul i64 %24, -4132994306676758123 store i64 %25, ptr %id, align 8 %26 = load i8, ptr %i, align 1 %27 = add i8 %26, 1 store i8 %27, ptr %i, align 1 br label %while_cond while_end: ; preds = %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %i) call void @llvm.lifetime.end.p0(i64 -1, ptr %k) %28 = load i64, ptr %id, align 8 %zero_cond = icmp eq i64 %28, 0 br i1 %zero_cond, label %if_zero, label %if_end if_zero: ; preds = %while_end store i64 1, ptr %id, align 8 br label %if_end if_end: ; preds = %if_zero, %while_end %29 = load i64, ptr %id, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %id) ret i64 %29 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!94} !llvm.module.flags = !{!96} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 16, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !23 = !DIGlobalVariableExpression(var: !24, expr: !DIExpression()) !24 = distinct !DIGlobalVariable(name: "AT_z", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "stack_perf_127", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !28) !28 = !{!29, !34, !39, !44} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 288, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 9, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 4194304, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 131072, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 96, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 12, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !45, size: 64, offset: 192) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 8128, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 127, lowerBound: 0) !49 = !DIGlobalVariableExpression(var: !50, expr: !DIExpression()) !50 = distinct !DIGlobalVariable(name: "stack_bpftrace_6", linkageName: "global", scope: !2, file: !2, type: !51, isLocal: false, isDefinition: true) !51 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !52) !52 = !{!29, !34, !39, !53} !53 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !54, size: 64, offset: 192) !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 384, elements: !56) !56 = !{!57} !57 = !DISubrange(count: 6, lowerBound: 0) !58 = !DIGlobalVariableExpression(var: !59, expr: !DIExpression()) !59 = distinct !DIGlobalVariable(name: "stack_bpftrace_127", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !60 = !DIGlobalVariableExpression(var: !61, expr: !DIExpression()) !61 = distinct !DIGlobalVariable(name: "stack_scratch", linkageName: "global", scope: !2, file: !2, type: !62, isLocal: false, isDefinition: true) !62 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !63) !63 = !{!64, !11, !67, !44} !64 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !65, size: 64) !65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) !66 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !56) !67 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !68, size: 64, offset: 128) !68 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !69, size: 64) !69 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !70 = !DIGlobalVariableExpression(var: !71, expr: !DIExpression()) !71 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !72, isLocal: false, isDefinition: true) !72 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !73) !73 = !{!74, !79} !74 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !75, size: 64) !75 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !76, size: 64) !76 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !77) !77 = !{!78} !78 = !DISubrange(count: 27, lowerBound: 0) !79 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !80, size: 64, offset: 64) !80 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !81, size: 64) !81 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !82) !82 = !{!83} !83 = !DISubrange(count: 262144, lowerBound: 0) !84 = !DIGlobalVariableExpression(var: !85, expr: !DIExpression()) !85 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !86, isLocal: false, isDefinition: true) !86 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !87) !87 = !{!88, !11, !67, !93} !88 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !89, size: 64) !89 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !90, size: 64) !90 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !91) !91 = !{!92} !92 = !DISubrange(count: 2, lowerBound: 0) !93 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !94 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !95) !95 = !{!0, !21, !23, !25, !49, !58, !60, !70, !84} !96 = !{i32 2, !"Debug Info Version", i32 3} !97 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !98, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !94, retainedNodes: !101) !98 = !DISubroutineType(types: !99) !99 = !{!14, !100} !100 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !101 = !{!102} !102 = !DILocalVariable(name: "ctx", arg: 1, scope: !97, file: !2, type: !100) bpftrace-0.23.2/tests/codegen/llvm/call_len_for_each_map_elem.ll000066400000000000000000000161761477746507000247270ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !50 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define i64 @kprobe_f_2(ptr %0) section "s_kprobe_f_2" !dbg !57 { entry: %"$s" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$s") store i64 0, ptr %"$s", align 8 %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_x, ptr @map_len_cb, ptr null, i64 0) store i64 %for_each_map_elem, ptr %"$s", align 8 ret i64 0 } define internal i64 @map_len_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !60 { ret i64 0 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) !57 = distinct !DISubprogram(name: "kprobe_f_2", linkageName: "kprobe_f_2", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !58) !58 = !{!59} !59 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !53) !60 = distinct !DISubprogram(name: "map_len_cb", linkageName: "map_len_cb", scope: !2, file: !2, type: !61, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !47, retainedNodes: !63) !61 = !DISubroutineType(types: !62) !62 = !{!18, !53, !53, !53, !53} !63 = !{!64, !65, !66, !67} !64 = !DILocalVariable(name: "map", arg: 1, scope: !60, file: !2, type: !53) !65 = !DILocalVariable(name: "key", arg: 2, scope: !60, file: !2, type: !53) !66 = !DILocalVariable(name: "value", arg: 3, scope: !60, file: !2, type: !53) !67 = !DILocalVariable(name: "ctx", arg: 4, scope: !60, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/call_len_map_sum_elem_count.ll000066400000000000000000000155341477746507000251720ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !50 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define i64 @kprobe_f_2(ptr %0) section "s_kprobe_f_2" !dbg !57 { entry: %"$s" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$s") store i64 0, ptr %"$s", align 8 %len = call i64 @bpf_map_sum_elem_count(ptr @AT_x) store i64 %len, ptr %"$s", align 8 ret i64 0 } declare !dbg !60 extern_weak i64 @bpf_map_sum_elem_count(ptr %0) local_unnamed_addr section ".ksyms" attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) !57 = distinct !DISubprogram(name: "kprobe_f_2", linkageName: "kprobe_f_2", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !58) !58 = !{!59} !59 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !53) !60 = !DISubprogram(name: "bpf_map_sum_elem_count", linkageName: "bpf_map_sum_elem_count", scope: !2, file: !2, type: !61, flags: DIFlagPrototyped, spFlags: 0) !61 = !DISubroutineType(types: !62) !62 = !{!18, !63} !63 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !64, size: 64) !64 = !DICompositeType(tag: DW_TAG_structure_type, name: "bpf_map", scope: !2, file: !2, elements: !65) !65 = !{} bpftrace-0.23.2/tests/codegen/llvm/call_len_ustack_kstack.ll000066400000000000000000000413331477746507000241450ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %"struct map_t.3" = type { ptr, ptr } %"struct map_t.4" = type { ptr, ptr, ptr, ptr } %kstack_key = type { i64, i64 } %ustack_key = type { i64, i64, i32, i32 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @stack_bpftrace_127 = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @stack_scratch = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !43 @ringbuf = dso_local global %"struct map_t.3" zeroinitializer, section ".maps", !dbg !55 @event_loss_counter = dso_local global %"struct map_t.4" zeroinitializer, section ".maps", !dbg !69 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !81 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %lookup_stack_scratch_key5 = alloca i32, align 4 %stack_key2 = alloca %kstack_key, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %lookup_stack_scratch_key = alloca i32, align 4 %stack_key = alloca %ustack_key, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key) call void @llvm.memset.p0.i64(ptr align 1 %stack_key, i8 0, i64 24, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key) store i32 0, ptr %lookup_stack_scratch_key, align 4 %lookup_stack_scratch_map = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key) %lookup_stack_scratch_cond = icmp ne ptr %lookup_stack_scratch_map, null br i1 %lookup_stack_scratch_cond, label %lookup_stack_scratch_merge, label %lookup_stack_scratch_failure stack_scratch_failure: ; preds = %lookup_stack_scratch_failure br label %merge_block merge_block: ; preds = %stack_scratch_failure, %get_stack_success, %get_stack_fail %1 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 2 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %2 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %2 to i32 store i32 %pid, ptr %1, align 4 %3 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 3 store i32 0, ptr %3, align 4 %4 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 1 %5 = load i64, ptr %4, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %5, ptr %"@x_val", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key2) call void @llvm.memset.p0.i64(ptr align 1 %stack_key2, i8 0, i64 16, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key5) store i32 0, ptr %lookup_stack_scratch_key5, align 4 %lookup_stack_scratch_map6 = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key5) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key5) %lookup_stack_scratch_cond9 = icmp ne ptr %lookup_stack_scratch_map6, null br i1 %lookup_stack_scratch_cond9, label %lookup_stack_scratch_merge8, label %lookup_stack_scratch_failure7 lookup_stack_scratch_failure: ; preds = %entry br label %stack_scratch_failure lookup_stack_scratch_merge: ; preds = %entry %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map, i32 1016, ptr null) %get_stack = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map, i32 1016, i64 256) %6 = icmp sge i64 %get_stack, 0 br i1 %6, label %get_stack_success, label %get_stack_fail get_stack_success: ; preds = %lookup_stack_scratch_merge %7 = udiv i64 %get_stack, 8 %8 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 1 store i64 %7, ptr %8, align 8 %9 = trunc i64 %7 to i8 %murmur_hash_2 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map, i8 %9, i64 1) %10 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 0 store i64 %murmur_hash_2, ptr %10, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_127, ptr %stack_key, ptr %lookup_stack_scratch_map, i64 0) br label %merge_block get_stack_fail: ; preds = %lookup_stack_scratch_merge br label %merge_block stack_scratch_failure3: ; preds = %lookup_stack_scratch_failure7 br label %merge_block4 merge_block4: ; preds = %stack_scratch_failure3, %get_stack_success11, %get_stack_fail12 %11 = getelementptr %kstack_key, ptr %stack_key2, i64 0, i32 1 %12 = load i64, ptr %11, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") store i64 %12, ptr %"@y_val", align 8 %update_elem16 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 lookup_stack_scratch_failure7: ; preds = %merge_block br label %stack_scratch_failure3 lookup_stack_scratch_merge8: ; preds = %merge_block %probe_read_kernel10 = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map6, i32 1016, ptr null) %get_stack13 = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map6, i32 1016, i64 0) %13 = icmp sge i64 %get_stack13, 0 br i1 %13, label %get_stack_success11, label %get_stack_fail12 get_stack_success11: ; preds = %lookup_stack_scratch_merge8 %14 = udiv i64 %get_stack13, 8 %15 = getelementptr %kstack_key, ptr %stack_key2, i64 0, i32 1 store i64 %14, ptr %15, align 8 %16 = trunc i64 %14 to i8 %murmur_hash_214 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map6, i8 %16, i64 1) %17 = getelementptr %kstack_key, ptr %stack_key2, i64 0, i32 0 store i64 %murmur_hash_214, ptr %17, align 8 %update_elem15 = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_127, ptr %stack_key2, ptr %lookup_stack_scratch_map6, i64 0) br label %merge_block4 get_stack_fail12: ; preds = %lookup_stack_scratch_merge8 br label %merge_block4 } ; Function Attrs: alwaysinline define internal i64 @murmur_hash_2(ptr %0, i8 %1, i64 %2) #1 section "helpers" { entry: %k = alloca i64, align 8 %i = alloca i8, align 1 %id = alloca i64, align 8 %seed_addr = alloca i64, align 8 %nr_stack_frames_addr = alloca i8, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %id) call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %k) store i8 %1, ptr %nr_stack_frames_addr, align 1 store i64 %2, ptr %seed_addr, align 8 %3 = load i8, ptr %nr_stack_frames_addr, align 1 %4 = zext i8 %3 to i64 %5 = mul i64 %4, -4132994306676758123 %6 = load i64, ptr %seed_addr, align 8 %7 = xor i64 %6, %5 store i64 %7, ptr %id, align 8 store i8 0, ptr %i, align 1 br label %while_cond while_cond: ; preds = %while_body, %entry %8 = load i8, ptr %nr_stack_frames_addr, align 1 %9 = load i8, ptr %i, align 1 %length.cmp = icmp ult i8 %9, %8 br i1 %length.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %10 = load i8, ptr %i, align 1 %11 = getelementptr i64, ptr %0, i8 %10 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %k, align 8 %13 = load i64, ptr %k, align 8 %14 = mul i64 %13, -4132994306676758123 store i64 %14, ptr %k, align 8 %15 = load i64, ptr %k, align 8 %16 = lshr i64 %15, 47 %17 = load i64, ptr %k, align 8 %18 = xor i64 %17, %16 store i64 %18, ptr %k, align 8 %19 = load i64, ptr %k, align 8 %20 = mul i64 %19, -4132994306676758123 store i64 %20, ptr %k, align 8 %21 = load i64, ptr %k, align 8 %22 = load i64, ptr %id, align 8 %23 = xor i64 %22, %21 store i64 %23, ptr %id, align 8 %24 = load i64, ptr %id, align 8 %25 = mul i64 %24, -4132994306676758123 store i64 %25, ptr %id, align 8 %26 = load i8, ptr %i, align 1 %27 = add i8 %26, 1 store i8 %27, ptr %i, align 1 br label %while_cond while_end: ; preds = %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %i) call void @llvm.lifetime.end.p0(i64 -1, ptr %k) %28 = load i64, ptr %id, align 8 %zero_cond = icmp eq i64 %28, 0 br i1 %zero_cond, label %if_zero, label %if_end if_zero: ; preds = %while_end store i64 1, ptr %id, align 8 br label %if_end if_end: ; preds = %if_zero, %while_end %29 = load i64, ptr %id, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %id) ret i64 %29 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!78} !llvm.module.flags = !{!80} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "stack_bpftrace_127", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !21) !21 = !{!22, !27, !32, !38} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 288, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 9, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 4194304, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 131072, lowerBound: 0) !32 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !33, size: 64, offset: 128) !33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !34, size: 64) !34 = !DICompositeType(tag: DW_TAG_array_type, baseType: !35, size: 96, elements: !36) !35 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !36 = !{!37} !37 = !DISubrange(count: 12, lowerBound: 0) !38 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !39, size: 64, offset: 192) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 8128, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 127, lowerBound: 0) !43 = !DIGlobalVariableExpression(var: !44, expr: !DIExpression()) !44 = distinct !DIGlobalVariable(name: "stack_scratch", linkageName: "global", scope: !2, file: !2, type: !45, isLocal: false, isDefinition: true) !45 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !46) !46 = !{!47, !11, !52, !38} !47 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !48, size: 64) !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !50) !50 = !{!51} !51 = !DISubrange(count: 6, lowerBound: 0) !52 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !53, size: 64, offset: 128) !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !55 = !DIGlobalVariableExpression(var: !56, expr: !DIExpression()) !56 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !57, isLocal: false, isDefinition: true) !57 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !58) !58 = !{!59, !64} !59 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !60, size: 64) !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !61, size: 64) !61 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !62) !62 = !{!63} !63 = !DISubrange(count: 27, lowerBound: 0) !64 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !65, size: 64, offset: 64) !65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) !66 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !67) !67 = !{!68} !68 = !DISubrange(count: 262144, lowerBound: 0) !69 = !DIGlobalVariableExpression(var: !70, expr: !DIExpression()) !70 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !71, isLocal: false, isDefinition: true) !71 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !72) !72 = !{!73, !11, !52, !15} !73 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !74, size: 64) !74 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !75, size: 64) !75 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !76) !76 = !{!77} !77 = !DISubrange(count: 2, lowerBound: 0) !78 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !79) !79 = !{!0, !16, !18, !43, !55, !69} !80 = !{i32 2, !"Debug Info Version", i32 3} !81 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !82, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !78, retainedNodes: !85) !82 = !DISubroutineType(types: !83) !83 = !{!14, !84} !84 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !85 = !{!86} !86 = !DILocalVariable(name: "ctx", arg: 1, scope: !81, file: !2, type: !84) bpftrace-0.23.2/tests/codegen/llvm/call_lhist.ll000066400000000000000000000213511477746507000215760ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !54 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %linear = call i64 @linear(i64 %2, i64 0, i64 100, i64 1) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 %linear, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %3 = load i64, ptr %lookup_elem, align 8 %4 = add i64 %3, 1 store i64 %4, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: alwaysinline define internal i64 @linear(i64 %0, i64 %1, i64 %2, i64 %3) #1 section "helpers" { entry: %4 = alloca i64, align 8 %5 = alloca i64, align 8 %6 = alloca i64, align 8 %7 = alloca i64, align 8 %8 = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %8) call void @llvm.lifetime.start.p0(i64 -1, ptr %7) call void @llvm.lifetime.start.p0(i64 -1, ptr %6) call void @llvm.lifetime.start.p0(i64 -1, ptr %5) call void @llvm.lifetime.start.p0(i64 -1, ptr %4) store i64 %0, ptr %8, align 8 store i64 %1, ptr %7, align 8 store i64 %2, ptr %6, align 8 store i64 %3, ptr %5, align 8 %9 = load i64, ptr %7, align 8 %10 = load i64, ptr %8, align 8 %11 = icmp slt i64 %10, %9 br i1 %11, label %lhist.lt_min, label %lhist.ge_min lhist.lt_min: ; preds = %entry ret i64 0 lhist.ge_min: ; preds = %entry %12 = load i64, ptr %6, align 8 %13 = load i64, ptr %8, align 8 %14 = icmp sgt i64 %13, %12 br i1 %14, label %lhist.gt_max, label %lhist.le_max lhist.le_max: ; preds = %lhist.ge_min %15 = load i64, ptr %5, align 8 %16 = load i64, ptr %7, align 8 %17 = load i64, ptr %8, align 8 %18 = sub i64 %17, %16 %19 = udiv i64 %18, %15 %20 = add i64 %19, 1 store i64 %20, ptr %4, align 8 %21 = load i64, ptr %4, align 8 ret i64 %21 lhist.gt_max: ; preds = %lhist.ge_min %22 = load i64, ptr %5, align 8 %23 = load i64, ptr %7, align 8 %24 = load i64, ptr %6, align 8 %25 = sub i64 %24, %23 %26 = udiv i64 %25, %22 %27 = add i64 %26, 1 store i64 %27, ptr %4, align 8 %28 = load i64, ptr %4, align 8 ret i64 %28 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!51} !llvm.module.flags = !{!53} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !48, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !44, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 1, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !49, size: 64, offset: 128) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !51 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !52) !52 = !{!0, !20, !34} !53 = !{i32 2, !"Debug Info Version", i32 3} !54 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !55, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !51, retainedNodes: !59) !55 = !DISubroutineType(types: !56) !56 = !{!18, !57} !57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !58, size: 64) !58 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !54, file: !2, type: !57) bpftrace-0.23.2/tests/codegen/llvm/call_macaddr.ll000066400000000000000000000147571477746507000220620ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@x_key" = alloca i64, align 8 %macaddr = alloca [6 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %macaddr) call void @llvm.memset.p0.i64(ptr align 1 %macaddr, i8 0, i64 6, i1 false) %1 = call ptr @llvm.preserve.static.offset(ptr null) %2 = getelementptr i8, ptr %1, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %macaddr, i32 6, ptr %2) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %macaddr, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %macaddr) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 48, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 6, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/call_map_key_scratch_buf.ll000066400000000000000000000332111477746507000244410ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !22 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !36 @map_key_buf = dso_local externally_initialized global [1 x [7 x [8 x i8]]] zeroinitializer, section ".data.map_key_buf", !dbg !53 @write_map_val_buf = dso_local externally_initialized global [1 x [1 x [8 x i8]]] zeroinitializer, section ".data.write_map_val_buf", !dbg !63 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !67 @read_map_val_buf = dso_local externally_initialized global [1 x [2 x [8 x i8]]] zeroinitializer, section ".data.read_map_val_buf", !dbg !69 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !73 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !78 { entry: %initial_value9 = alloca i64, align 8 %lookup_elem_val7 = alloca i64, align 8 %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [7 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 store i64 1, ptr %2, align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %2) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %3 = load i64, ptr %lookup_elem, align 8 %4 = add i64 %3, 1 store i64 %4, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %2, ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) %log2 = call i64 @log2(i64 10, i64 0) %get_cpu_id1 = call i64 inttoptr (i64 8 to ptr)() %5 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded2 = and i64 %get_cpu_id1, %5 %6 = getelementptr [1 x [7 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded2, i64 1, i64 0 store i64 %log2, ptr %6, align 8 %lookup_elem3 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_y, ptr %6) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val7) %map_lookup_cond8 = icmp ne ptr %lookup_elem3, null br i1 %map_lookup_cond8, label %lookup_success4, label %lookup_failure5 lookup_success4: ; preds = %lookup_merge %7 = load i64, ptr %lookup_elem3, align 8 %8 = add i64 %7, 1 store i64 %8, ptr %lookup_elem3, align 8 br label %lookup_merge6 lookup_failure5: ; preds = %lookup_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value9) store i64 1, ptr %initial_value9, align 8 %update_elem10 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %6, ptr %initial_value9, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value9) br label %lookup_merge6 lookup_merge6: ; preds = %lookup_failure5, %lookup_success4 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val7) %get_cpu_id11 = call i64 inttoptr (i64 8 to ptr)() %9 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded12 = and i64 %get_cpu_id11, %9 %10 = getelementptr [1 x [7 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded12, i64 2, i64 0 store i64 1, ptr %10, align 8 %lookup_elem13 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %10) %has_key = icmp ne ptr %lookup_elem13, null %get_cpu_id14 = call i64 inttoptr (i64 8 to ptr)() %11 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded15 = and i64 %get_cpu_id14, %11 %12 = getelementptr [1 x [7 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded15, i64 3, i64 0 store i64 1, ptr %12, align 8 %delete_elem = call i64 inttoptr (i64 3 to ptr)(ptr @AT_x, ptr %12) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: alwaysinline define internal i64 @log2(i64 %0, i64 %1) #2 section "helpers" { entry: %2 = alloca i64, align 8 %3 = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %3) store i64 %0, ptr %3, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %2) store i64 %1, ptr %2, align 8 %4 = load i64, ptr %3, align 8 %5 = icmp slt i64 %4, 0 br i1 %5, label %hist.is_less_than_zero, label %hist.is_not_less_than_zero hist.is_less_than_zero: ; preds = %entry ret i64 0 hist.is_not_less_than_zero: ; preds = %entry %6 = load i64, ptr %2, align 8 %7 = shl i64 1, %6 %8 = sub i64 %7, 1 %9 = icmp ule i64 %4, %8 br i1 %9, label %hist.is_zero, label %hist.is_not_zero hist.is_zero: ; preds = %hist.is_not_less_than_zero %10 = add i64 %4, 1 ret i64 %10 hist.is_not_zero: ; preds = %hist.is_not_less_than_zero %11 = icmp sge i64 %4, 4294967296 %12 = zext i1 %11 to i64 %13 = shl i64 %12, 5 %14 = lshr i64 %4, %13 %15 = add i64 0, %13 %16 = icmp sge i64 %14, 65536 %17 = zext i1 %16 to i64 %18 = shl i64 %17, 4 %19 = lshr i64 %14, %18 %20 = add i64 %15, %18 %21 = icmp sge i64 %19, 256 %22 = zext i1 %21 to i64 %23 = shl i64 %22, 3 %24 = lshr i64 %19, %23 %25 = add i64 %20, %23 %26 = icmp sge i64 %24, 16 %27 = zext i1 %26 to i64 %28 = shl i64 %27, 2 %29 = lshr i64 %24, %28 %30 = add i64 %25, %28 %31 = icmp sge i64 %29, 4 %32 = zext i1 %31 to i64 %33 = shl i64 %32, 1 %34 = lshr i64 %29, %33 %35 = add i64 %30, %33 %36 = icmp sge i64 %34, 2 %37 = zext i1 %36 to i64 %38 = shl i64 %37, 0 %39 = lshr i64 %34, %38 %40 = add i64 %35, %38 %41 = sub i64 %40, %6 %42 = load i64, ptr %3, align 8 %43 = lshr i64 %42, %41 %44 = and i64 %43, %8 %45 = add i64 %41, 1 %46 = shl i64 %45, %6 %47 = add i64 %46, %44 %48 = add i64 %47, 1 ret i64 %48 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { alwaysinline } !llvm.dbg.cu = !{!75} !llvm.module.flags = !{!77} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !22 = !DIGlobalVariableExpression(var: !23, expr: !DIExpression()) !23 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !24 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !25) !25 = !{!26, !31} !26 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !27, size: 64) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !29) !29 = !{!30} !30 = !DISubrange(count: 27, lowerBound: 0) !31 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !32, size: 64, offset: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 262144, lowerBound: 0) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !38, isLocal: false, isDefinition: true) !38 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !39) !39 = !{!40, !45, !50, !19} !40 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !41, size: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 2, lowerBound: 0) !45 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !46, size: 64, offset: 64) !46 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !47, size: 64) !47 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !48) !48 = !{!49} !49 = !DISubrange(count: 1, lowerBound: 0) !50 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !51, size: 64, offset: 128) !51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !52, size: 64) !52 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !53 = !DIGlobalVariableExpression(var: !54, expr: !DIExpression()) !54 = distinct !DIGlobalVariable(name: "map_key_buf", linkageName: "global", scope: !2, file: !2, type: !55, isLocal: false, isDefinition: true) !55 = !DICompositeType(tag: DW_TAG_array_type, baseType: !56, size: 448, elements: !48) !56 = !DICompositeType(tag: DW_TAG_array_type, baseType: !57, size: 448, elements: !61) !57 = !DICompositeType(tag: DW_TAG_array_type, baseType: !58, size: 64, elements: !59) !58 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !59 = !{!60} !60 = !DISubrange(count: 8, lowerBound: 0) !61 = !{!62} !62 = !DISubrange(count: 7, lowerBound: 0) !63 = !DIGlobalVariableExpression(var: !64, expr: !DIExpression()) !64 = distinct !DIGlobalVariable(name: "write_map_val_buf", linkageName: "global", scope: !2, file: !2, type: !65, isLocal: false, isDefinition: true) !65 = !DICompositeType(tag: DW_TAG_array_type, baseType: !66, size: 64, elements: !48) !66 = !DICompositeType(tag: DW_TAG_array_type, baseType: !57, size: 64, elements: !48) !67 = !DIGlobalVariableExpression(var: !68, expr: !DIExpression()) !68 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !69 = !DIGlobalVariableExpression(var: !70, expr: !DIExpression()) !70 = distinct !DIGlobalVariable(name: "read_map_val_buf", linkageName: "global", scope: !2, file: !2, type: !71, isLocal: false, isDefinition: true) !71 = !DICompositeType(tag: DW_TAG_array_type, baseType: !72, size: 128, elements: !48) !72 = !DICompositeType(tag: DW_TAG_array_type, baseType: !57, size: 128, elements: !43) !73 = !DIGlobalVariableExpression(var: !74, expr: !DIExpression()) !74 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !75 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !76) !76 = !{!0, !20, !22, !36, !53, !63, !67, !69, !73} !77 = !{i32 2, !"Debug Info Version", i32 3} !78 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !79, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !75, retainedNodes: !82) !79 = !DISubroutineType(types: !80) !80 = !{!18, !81} !81 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !58, size: 64) !82 = !{!83} !83 = !DILocalVariable(name: "ctx", arg: 1, scope: !78, file: !2, type: !81) bpftrace-0.23.2/tests/codegen/llvm/call_map_key_stack.ll000066400000000000000000000264221477746507000232710ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !22 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !36 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !53 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !58 { entry: %"@x_key11" = alloca i64, align 8 %"@x_key9" = alloca i64, align 8 %initial_value7 = alloca i64, align 8 %lookup_elem_val5 = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 %2 = add i64 %1, 1 store i64 %2, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %log2 = call i64 @log2(i64 10, i64 0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 %log2, ptr %"@y_key", align 8 %lookup_elem1 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_y, ptr %"@y_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val5) %map_lookup_cond6 = icmp ne ptr %lookup_elem1, null br i1 %map_lookup_cond6, label %lookup_success2, label %lookup_failure3 lookup_success2: ; preds = %lookup_merge %3 = load i64, ptr %lookup_elem1, align 8 %4 = add i64 %3, 1 store i64 %4, ptr %lookup_elem1, align 8 br label %lookup_merge4 lookup_failure3: ; preds = %lookup_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value7) store i64 1, ptr %initial_value7, align 8 %update_elem8 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %initial_value7, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value7) br label %lookup_merge4 lookup_merge4: ; preds = %lookup_failure3, %lookup_success2 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val5) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key9") store i64 1, ptr %"@x_key9", align 8 %lookup_elem10 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key9") %has_key = icmp ne ptr %lookup_elem10, null call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key9") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key11") store i64 1, ptr %"@x_key11", align 8 %delete_elem = call i64 inttoptr (i64 3 to ptr)(ptr @AT_x, ptr %"@x_key11") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key11") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: alwaysinline define internal i64 @log2(i64 %0, i64 %1) #2 section "helpers" { entry: %2 = alloca i64, align 8 %3 = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %3) store i64 %0, ptr %3, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %2) store i64 %1, ptr %2, align 8 %4 = load i64, ptr %3, align 8 %5 = icmp slt i64 %4, 0 br i1 %5, label %hist.is_less_than_zero, label %hist.is_not_less_than_zero hist.is_less_than_zero: ; preds = %entry ret i64 0 hist.is_not_less_than_zero: ; preds = %entry %6 = load i64, ptr %2, align 8 %7 = shl i64 1, %6 %8 = sub i64 %7, 1 %9 = icmp ule i64 %4, %8 br i1 %9, label %hist.is_zero, label %hist.is_not_zero hist.is_zero: ; preds = %hist.is_not_less_than_zero %10 = add i64 %4, 1 ret i64 %10 hist.is_not_zero: ; preds = %hist.is_not_less_than_zero %11 = icmp sge i64 %4, 4294967296 %12 = zext i1 %11 to i64 %13 = shl i64 %12, 5 %14 = lshr i64 %4, %13 %15 = add i64 0, %13 %16 = icmp sge i64 %14, 65536 %17 = zext i1 %16 to i64 %18 = shl i64 %17, 4 %19 = lshr i64 %14, %18 %20 = add i64 %15, %18 %21 = icmp sge i64 %19, 256 %22 = zext i1 %21 to i64 %23 = shl i64 %22, 3 %24 = lshr i64 %19, %23 %25 = add i64 %20, %23 %26 = icmp sge i64 %24, 16 %27 = zext i1 %26 to i64 %28 = shl i64 %27, 2 %29 = lshr i64 %24, %28 %30 = add i64 %25, %28 %31 = icmp sge i64 %29, 4 %32 = zext i1 %31 to i64 %33 = shl i64 %32, 1 %34 = lshr i64 %29, %33 %35 = add i64 %30, %33 %36 = icmp sge i64 %34, 2 %37 = zext i1 %36 to i64 %38 = shl i64 %37, 0 %39 = lshr i64 %34, %38 %40 = add i64 %35, %38 %41 = sub i64 %40, %6 %42 = load i64, ptr %3, align 8 %43 = lshr i64 %42, %41 %44 = and i64 %43, %8 %45 = add i64 %41, 1 %46 = shl i64 %45, %6 %47 = add i64 %46, %44 %48 = add i64 %47, 1 ret i64 %48 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { alwaysinline } !llvm.dbg.cu = !{!55} !llvm.module.flags = !{!57} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !22 = !DIGlobalVariableExpression(var: !23, expr: !DIExpression()) !23 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !24 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !25) !25 = !{!26, !31} !26 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !27, size: 64) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !29) !29 = !{!30} !30 = !DISubrange(count: 27, lowerBound: 0) !31 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !32, size: 64, offset: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 262144, lowerBound: 0) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !38, isLocal: false, isDefinition: true) !38 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !39) !39 = !{!40, !45, !50, !19} !40 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !41, size: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 2, lowerBound: 0) !45 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !46, size: 64, offset: 64) !46 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !47, size: 64) !47 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !48) !48 = !{!49} !49 = !DISubrange(count: 1, lowerBound: 0) !50 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !51, size: 64, offset: 128) !51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !52, size: 64) !52 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !53 = !DIGlobalVariableExpression(var: !54, expr: !DIExpression()) !54 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !55 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !56) !56 = !{!0, !20, !22, !36, !53} !57 = !{i32 2, !"Debug Info Version", i32 3} !58 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !59, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !55, retainedNodes: !63) !59 = !DISubroutineType(types: !60) !60 = !{!18, !61} !61 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !62, size: 64) !62 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !63 = !{!64} !64 = !DILocalVariable(name: "ctx", arg: 1, scope: !58, file: !2, type: !61) bpftrace-0.23.2/tests/codegen/llvm/call_max.ll000066400000000000000000000177331477746507000212510ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %min_max_val = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !52 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %mm_struct = alloca %min_max_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %3 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 %4 = load i64, ptr %3, align 8 %5 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 %6 = load i64, ptr %5, align 8 %is_set_cond = icmp eq i64 %6, 1 br i1 %is_set_cond, label %is_set, label %min_max lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %mm_struct) %7 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 0 store i64 %2, ptr %7, align 8 %8 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 1 store i64 1, ptr %8, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %mm_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %mm_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %min_max, %is_set call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 is_set: ; preds = %lookup_success %9 = icmp uge i64 %2, %4 br i1 %9, label %min_max, label %lookup_merge min_max: ; preds = %is_set, %lookup_success %10 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 store i64 %2, ptr %10, align 8 %11 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 store i64 1, ptr %11, align 8 br label %lookup_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !51} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !51 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !52 = !DIGlobalVariableExpression(var: !53, expr: !DIExpression()) !53 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !26, !40, !52} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !62) !58 = !DISubroutineType(types: !59) !59 = !{!18, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !61, size: 64) !61 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !62 = !{!63} !63 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/call_min.ll000066400000000000000000000177331477746507000212470ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %min_max_val = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !52 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %mm_struct = alloca %min_max_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %3 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 %4 = load i64, ptr %3, align 8 %5 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 %6 = load i64, ptr %5, align 8 %is_set_cond = icmp eq i64 %6, 1 br i1 %is_set_cond, label %is_set, label %min_max lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %mm_struct) %7 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 0 store i64 %2, ptr %7, align 8 %8 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 1 store i64 1, ptr %8, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %mm_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %mm_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %min_max, %is_set call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 is_set: ; preds = %lookup_success %9 = icmp uge i64 %4, %2 br i1 %9, label %min_max, label %lookup_merge min_max: ; preds = %is_set, %lookup_success %10 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 store i64 %2, ptr %10, align 8 %11 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 store i64 1, ptr %11, align 8 br label %lookup_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !51} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !51 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !52 = !DIGlobalVariableExpression(var: !53, expr: !DIExpression()) !53 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !26, !40, !52} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !62) !58 = !DISubroutineType(types: !59) !59 = !{!18, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !61, size: 64) !61 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !62 = !{!63} !63 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/call_nsecs_boot.ll000066400000000000000000000130211477746507000226040ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_ns = call i64 inttoptr (i64 125 to ptr)() call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_ns, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_nsecs_monotonic.ll000066400000000000000000000130171477746507000236530ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_ns = call i64 inttoptr (i64 5 to ptr)() call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_ns, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_nsecs_tai.ll000066400000000000000000000130211477746507000224160ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_ns = call i64 inttoptr (i64 208 to ptr)() call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %get_ns, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_ntop_char16.ll000066400000000000000000000152001477746507000225730ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %inet = type { i64, [16 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@x_key" = alloca i64, align 8 %inet = alloca %inet, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %inet) %1 = getelementptr %inet, ptr %inet, i64 0, i32 0 store i64 10, ptr %1, align 8 %2 = getelementptr %inet, ptr %inet, i32 0, i32 1 call void @llvm.memset.p0.i64(ptr align 1 %2, i8 0, i64 16, i1 false) %3 = call ptr @llvm.preserve.static.offset(ptr null) %4 = getelementptr i8, ptr %3, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 16, ptr %4) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %inet, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %inet) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 192, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 24, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/call_ntop_char4.ll000066400000000000000000000151761477746507000225240ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %inet = type { i64, [16 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@x_key" = alloca i64, align 8 %inet = alloca %inet, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %inet) %1 = getelementptr %inet, ptr %inet, i64 0, i32 0 store i64 2, ptr %1, align 8 %2 = getelementptr %inet, ptr %inet, i32 0, i32 1 call void @llvm.memset.p0.i64(ptr align 1 %2, i8 0, i64 16, i1 false) %3 = call ptr @llvm.preserve.static.offset(ptr null) %4 = getelementptr i8, ptr %3, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 4, ptr %4) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %inet, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %inet) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 192, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 24, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/call_ntop_key.ll000066400000000000000000000147371477746507000223150ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %inet = type { i64, [16 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !55 { entry: %"@x_val" = alloca i64, align 8 %inet = alloca %inet, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %inet) %1 = getelementptr %inet, ptr %inet, i64 0, i32 0 store i64 2, ptr %1, align 8 %2 = getelementptr %inet, ptr %inet, i32 0, i32 1 call void @llvm.memset.p0.i64(ptr align 1 %2, i8 0, i64 16, i1 false) store i32 -1, ptr %2, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %inet, ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %inet) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!52} !llvm.module.flags = !{!54} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !22} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 192, elements: !20) !19 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !20 = !{!21} !21 = !DISubrange(count: 24, lowerBound: 0) !22 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !23, size: 64, offset: 192) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !48, !49, !22} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !52 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !53) !53 = !{!0, !25, !39} !54 = !{i32 2, !"Debug Info Version", i32 3} !55 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !52, retainedNodes: !59) !56 = !DISubroutineType(types: !57) !57 = !{!24, !58} !58 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !55, file: !2, type: !58) bpftrace-0.23.2/tests/codegen/llvm/call_offsetof.ll000066400000000000000000000157501477746507000222740ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %exit_t = type <{ i64, i8 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !45 { entry: %key = alloca i32, align 4 %exit = alloca %exit_t, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 0, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %exit) %1 = getelementptr %exit_t, ptr %exit, i64 0, i32 0 store i64 30000, ptr %1, align 8 %2 = getelementptr %exit_t, ptr %exit, i64 0, i32 1 store i8 0, ptr %2, align 1 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %exit, i64 9, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %exit) ret i64 0 lookup_success: ; preds = %event_loss_counter %3 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge deadcode: ; No predecessors! ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_offsetof_sub_field.ll000066400000000000000000000157501477746507000243100ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %exit_t = type <{ i64, i8 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !45 { entry: %key = alloca i32, align 4 %exit = alloca %exit_t, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 0, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %exit) %1 = getelementptr %exit_t, ptr %exit, i64 0, i32 0 store i64 30000, ptr %1, align 8 %2 = getelementptr %exit_t, ptr %exit, i64 0, i32 1 store i8 0, ptr %2, align 1 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %exit, i64 9, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %exit) ret i64 0 lookup_success: ; preds = %event_loss_counter %3 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge deadcode: ; No predecessors! ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_override.ll000066400000000000000000000105661477746507000223000ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %override = call i64 inttoptr (i64 58 to ptr)(ptr %0, i64 %arg0) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_override_literal.ll000066400000000000000000000077661477746507000240240ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %override = call i64 inttoptr (i64 58 to ptr)(ptr %0, i64 -1) ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_path.ll000066400000000000000000000126071477746507000214130ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !38 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @fentry_mock_vmlinux_filp_close_1(ptr %0) section "s_fentry_mock_vmlinux_filp_close_1" !dbg !49 { entry: %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %d_path = call i64 inttoptr (i64 147 to ptr)(ptr null, ptr %2, i32 1024) ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 8192, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 8192, elements: !28) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 8192, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 1024, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "fentry_mock_vmlinux_filp_close_1", linkageName: "fentry_mock_vmlinux_filp_close_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/call_path_with_optional_size.ll000066400000000000000000000126051477746507000254030ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !38 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @fentry_mock_vmlinux_filp_close_1(ptr %0) section "s_fentry_mock_vmlinux_filp_close_1" !dbg !49 { entry: %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %d_path = call i64 inttoptr (i64 147 to ptr)(ptr null, ptr %2, i32 48) ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 8192, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 8192, elements: !28) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 8192, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 1024, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "fentry_mock_vmlinux_filp_close_1", linkageName: "fentry_mock_vmlinux_filp_close_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/call_percpu_kaddr.ll000066400000000000000000000105001477746507000231100ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @process_counts = external global i64, section ".ksyms", !dbg !36 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !41 { entry: %per_cpu_ptr = call ptr inttoptr (i64 153 to ptr)(ptr @process_counts, i64 0) %1 = ptrtoint ptr %per_cpu_ptr to i64 ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!38} !llvm.module.flags = !{!40} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "process_counts", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !39) !39 = !{!0, !16, !36} !40 = !{i32 2, !"Debug Info Version", i32 3} !41 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !42, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !38, retainedNodes: !46) !42 = !DISubroutineType(types: !43) !43 = !{!35, !44} !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !46 = !{!47} !47 = !DILocalVariable(name: "ctx", arg: 1, scope: !41, file: !2, type: !44) bpftrace-0.23.2/tests/codegen/llvm/call_percpu_kaddr_this_cpu.ll000066400000000000000000000104731477746507000250170ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @process_counts = external global i64, section ".ksyms", !dbg !36 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !41 { entry: %this_cpu_ptr = call ptr inttoptr (i64 154 to ptr)(ptr @process_counts) %1 = ptrtoint ptr %this_cpu_ptr to i64 ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!38} !llvm.module.flags = !{!40} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "process_counts", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !39) !39 = !{!0, !16, !36} !40 = !{i32 2, !"Debug Info Version", i32 3} !41 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !42, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !38, retainedNodes: !46) !42 = !DISubroutineType(types: !43) !43 = !{!35, !44} !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !46 = !{!47} !47 = !DILocalVariable(name: "ctx", arg: 1, scope: !41, file: !2, type: !44) bpftrace-0.23.2/tests/codegen/llvm/call_print.ll000066400000000000000000000167651477746507000216240ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %print_t = type <{ i64, i32, i32, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define i64 @kprobe_f_2(ptr %0) section "s_kprobe_f_2" !dbg !52 { entry: %key = alloca i32, align 4 %"print_@x" = alloca %print_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"print_@x") %1 = getelementptr %print_t, ptr %"print_@x", i64 0, i32 0 store i64 30001, ptr %1, align 8 %2 = getelementptr %print_t, ptr %"print_@x", i64 0, i32 1 store i32 0, ptr %2, align 4 %3 = getelementptr %print_t, ptr %"print_@x", i64 0, i32 2 store i32 0, ptr %3, align 4 %4 = getelementptr %print_t, ptr %"print_@x", i64 0, i32 3 store i32 0, ptr %4, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %"print_@x", i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %"print_@x") ret i64 0 lookup_success: ; preds = %event_loss_counter %5 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) !52 = distinct !DISubprogram(name: "kprobe_f_2", linkageName: "kprobe_f_2", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !53) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !52, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_print_composit.ll000066400000000000000000000164011477746507000235240ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %print_tuple_16_t = type <{ i64, i64, [16 x i8] }> %"int64_string[4]__tuple_t" = type { i64, [4 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @abc = global [4 x i8] c"abc\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %print_tuple_16_t = alloca %print_tuple_16_t, align 8 %tuple = alloca %"int64_string[4]__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 16, i1 false) %1 = getelementptr %"int64_string[4]__tuple_t", ptr %tuple, i32 0, i32 0 store i64 1, ptr %1, align 8 %2 = getelementptr %"int64_string[4]__tuple_t", ptr %tuple, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %2, ptr align 1 @abc, i64 4, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %print_tuple_16_t) %3 = getelementptr %print_tuple_16_t, ptr %print_tuple_16_t, i64 0, i32 0 store i64 30007, ptr %3, align 8 %4 = getelementptr %print_tuple_16_t, ptr %print_tuple_16_t, i64 0, i32 1 store i64 0, ptr %4, align 8 %5 = getelementptr %print_tuple_16_t, ptr %print_tuple_16_t, i32 0, i32 2 call void @llvm.memset.p0.i64(ptr align 1 %5, i8 0, i64 16, i1 false) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %5, ptr align 1 %tuple, i64 16, i1 false) %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %print_tuple_16_t, i64 32, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %print_tuple_16_t) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) ret i64 0 lookup_success: ; preds = %event_loss_counter %6 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_print_int.ll000066400000000000000000000144441477746507000224660ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %print_int_8_t = type <{ i64, i64, [8 x i8] }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %print_int_8_t = alloca %print_int_8_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %print_int_8_t) %1 = getelementptr %print_int_8_t, ptr %print_int_8_t, i64 0, i32 0 store i64 30007, ptr %1, align 8 %2 = getelementptr %print_int_8_t, ptr %print_int_8_t, i64 0, i32 1 store i64 0, ptr %2, align 8 %3 = getelementptr %print_int_8_t, ptr %print_int_8_t, i32 0, i32 2 call void @llvm.memset.p0.i64(ptr align 1 %3, i8 0, i64 8, i1 false) store i64 3, ptr %3, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %print_int_8_t, i64 24, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %print_int_8_t) ret i64 0 lookup_success: ; preds = %event_loss_counter %4 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_printf.ll000066400000000000000000000173601477746507000217620ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %printf_t = type { i64, i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %"struct Foo.l" = alloca i64, align 8 %"struct Foo.c" = alloca i8, align 1 %printf_args = alloca %printf_t, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %printf_args) call void @llvm.memset.p0.i64(ptr align 1 %printf_args, i8 0, i64 24, i1 false) %3 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 0 store i64 0, ptr %3, align 8 %4 = load i64, ptr %"$foo", align 8 %5 = inttoptr i64 %4 to ptr %6 = call ptr @llvm.preserve.static.offset(ptr %5) %7 = getelementptr i8, ptr %6, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.c") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.c", i32 1, ptr %7) %8 = load i8, ptr %"struct Foo.c", align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.c") %9 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 1 %10 = sext i8 %8 to i64 store i64 %10, ptr %9, align 8 %11 = load i64, ptr %"$foo", align 8 %12 = inttoptr i64 %11 to ptr %13 = call ptr @llvm.preserve.static.offset(ptr %12) %14 = getelementptr i8, ptr %13, i64 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.l") %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.l", i32 8, ptr %14) %15 = load i64, ptr %"struct Foo.l", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.l") %16 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 2 store i64 %15, ptr %16, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %printf_args, i64 24, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %printf_args) ret i64 0 lookup_success: ; preds = %event_loss_counter %17 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_reg.ll000066400000000000000000000135371477746507000212370ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 16 %reg_ip = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %reg_ip, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_signal.ll000066400000000000000000000106101477746507000217240ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = trunc i64 %arg0 to i32 %signal = call i64 inttoptr (i64 109 to ptr)(i32 %3) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_signal_literal.ll000066400000000000000000000077541477746507000234570ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %signal = call i64 inttoptr (i64 109 to ptr)(i32 8) ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_signal_string_literal.ll000066400000000000000000000077541477746507000250450ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %signal = call i64 inttoptr (i64 109 to ptr)(i32 9) ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_sizeof.ll000066400000000000000000000127161477746507000217570ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 8, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_stats.ll000066400000000000000000000166231477746507000216170ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %avg_stas_val = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !55 { entry: %avg_struct = alloca %avg_stas_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %3 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 0 %4 = load i64, ptr %3, align 8 %5 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 1 %6 = load i64, ptr %5, align 8 %7 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 0 %8 = add i64 %4, %2 store i64 %8, ptr %7, align 8 %9 = getelementptr %avg_stas_val, ptr %lookup_elem, i64 0, i32 1 %10 = add i64 1, %6 store i64 %10, ptr %9, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %avg_struct) %11 = getelementptr %avg_stas_val, ptr %avg_struct, i64 0, i32 0 store i64 %2, ptr %11, align 8 %12 = getelementptr %avg_stas_val, ptr %avg_struct, i64 0, i32 1 store i64 1, ptr %12, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %avg_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %avg_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!52} !llvm.module.flags = !{!54} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !51} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !51 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !52 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !53) !53 = !{!0, !26, !40} !54 = !{i32 2, !"Debug Info Version", i32 3} !55 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !52, retainedNodes: !60) !56 = !DISubroutineType(types: !57) !57 = !{!18, !58} !58 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !59, size: 64) !59 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !60 = !{!61} !61 = !DILocalVariable(name: "ctx", arg: 1, scope: !55, file: !2, type: !58) bpftrace-0.23.2/tests/codegen/llvm/call_str.ll000066400000000000000000000163261477746507000212710ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !48 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !50 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %"@x_key" = alloca i64, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %3 = call ptr @llvm.preserve.static.offset(ptr %0) %4 = getelementptr i64, ptr %3, i64 14 %arg0 = load volatile i64, ptr %4, align 8 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %2, i32 1024, i64 %arg0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %2, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 8192, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 1024, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = !DIGlobalVariableExpression(var: !49, expr: !DIExpression()) !49 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !50 = !DIGlobalVariableExpression(var: !51, expr: !DIExpression()) !51 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !52, isLocal: false, isDefinition: true) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !53, size: 8192, elements: !9) !53 = !DICompositeType(tag: DW_TAG_array_type, baseType: !17, size: 8192, elements: !9) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !21, !35, !48, !50} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !61) !58 = !DISubroutineType(types: !59) !59 = !{!14, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/call_str_2_expr.ll000066400000000000000000000167611477746507000225530ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !48 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !50 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %"@x_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 13 %arg1 = load volatile i64, ptr %2, align 8 %str.min.cmp = icmp ule i64 %arg1, 1024 %str.min.select = select i1 %str.min.cmp, i64 %arg1, i64 1024 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %3 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %3 %4 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %4, i32 1024, ptr null) %5 = call ptr @llvm.preserve.static.offset(ptr %0) %6 = getelementptr i64, ptr %5, i64 14 %arg0 = load volatile i64, ptr %6, align 8 %7 = trunc i64 %str.min.select to i32 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %4, i32 %7, i64 %arg0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %4, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 8192, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 1024, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = !DIGlobalVariableExpression(var: !49, expr: !DIExpression()) !49 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !50 = !DIGlobalVariableExpression(var: !51, expr: !DIExpression()) !51 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !52, isLocal: false, isDefinition: true) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !53, size: 8192, elements: !9) !53 = !DICompositeType(tag: DW_TAG_array_type, baseType: !17, size: 8192, elements: !9) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !21, !35, !48, !50} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !61) !58 = !DISubroutineType(types: !59) !59 = !{!14, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/call_str_2_lit.ll000066400000000000000000000165421477746507000223620ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !48 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !50 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !60 { entry: %"@x_key" = alloca i64, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %3 = call ptr @llvm.preserve.static.offset(ptr %0) %4 = getelementptr i64, ptr %3, i64 14 %arg0 = load volatile i64, ptr %4, align 8 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %2, i32 6, i64 %arg0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %2, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!57} !llvm.module.flags = !{!59} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 48, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 6, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = !DIGlobalVariableExpression(var: !49, expr: !DIExpression()) !49 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !50 = !DIGlobalVariableExpression(var: !51, expr: !DIExpression()) !51 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !52, isLocal: false, isDefinition: true) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !53, size: 8192, elements: !9) !53 = !DICompositeType(tag: DW_TAG_array_type, baseType: !54, size: 8192, elements: !9) !54 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 8192, elements: !55) !55 = !{!56} !56 = !DISubrange(count: 1024, lowerBound: 0) !57 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !58) !58 = !{!0, !21, !35, !48, !50} !59 = !{i32 2, !"Debug Info Version", i32 3} !60 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !57, retainedNodes: !64) !61 = !DISubroutineType(types: !62) !62 = !{!14, !63} !63 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !64 = !{!65} !65 = !DILocalVariable(name: "ctx", arg: 1, scope: !60, file: !2, type: !63) bpftrace-0.23.2/tests/codegen/llvm/call_strftime.ll000066400000000000000000000116061477746507000223120ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %strftime_t = type <{ i32, i32, i64 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %strftime_args = alloca %strftime_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %strftime_args) %1 = getelementptr %strftime_t, ptr %strftime_args, i64 0, i32 0 store i32 0, ptr %1, align 4 %2 = getelementptr %strftime_t, ptr %strftime_args, i64 0, i32 1 store i32 1, ptr %2, align 4 %get_ns = call i64 inttoptr (i64 125 to ptr)() %3 = getelementptr %strftime_t, ptr %strftime_args, i64 0, i32 2 store i64 %get_ns, ptr %3, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %strftime_args) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_sum.ll000066400000000000000000000156171477746507000212670ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !46 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %3 = load i64, ptr %lookup_elem, align 8 %4 = add i64 %3, %2 store i64 %4, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 %2, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !11, !43, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !44, size: 64, offset: 128) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !46 = !DIGlobalVariableExpression(var: !47, expr: !DIExpression()) !47 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !20, !34, !46} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !56) !52 = !DISubroutineType(types: !53) !53 = !{!18, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !56 = !{!57} !57 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/call_system.ll000066400000000000000000000142401477746507000217760ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %system_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %system_args = alloca %system_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %system_args) call void @llvm.memset.p0.i64(ptr align 1 %system_args, i8 0, i64 16, i1 false) %1 = getelementptr %system_t, ptr %system_args, i32 0, i32 0 store i64 10000, ptr %1, align 8 %2 = getelementptr %system_t, ptr %system_args, i32 0, i32 1 store i64 100, ptr %2, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %system_args, i64 16, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %system_args) ret i64 0 lookup_success: ; preds = %event_loss_counter %3 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_time.ll000066400000000000000000000134541477746507000214160ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %time_t = type <{ i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %time_t = alloca %time_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %time_t) %1 = getelementptr %time_t, ptr %time_t, i64 0, i32 0 store i64 30004, ptr %1, align 8 %2 = getelementptr %time_t, ptr %time_t, i64 0, i32 1 store i32 0, ptr %2, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %time_t, i64 12, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %time_t) ret i64 0 lookup_success: ; preds = %event_loss_counter %3 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/call_uptr_1.ll000066400000000000000000000141551477746507000216710ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %deref = alloca i16, align 2 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %deref) %probe_read_user = call i64 inttoptr (i64 112 to ptr)(ptr %deref, i32 2, i64 %arg0) %3 = load i16, ptr %deref, align 2 call void @llvm.lifetime.end.p0(i64 -1, ptr %deref) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") %4 = sext i16 %3 to i64 store i64 %4, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_uptr_2.ll000066400000000000000000000141551477746507000216720ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %deref = alloca i32, align 4 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %deref) %probe_read_user = call i64 inttoptr (i64 112 to ptr)(ptr %deref, i32 4, i64 %arg0) %3 = load i32, ptr %deref, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %deref) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") %4 = sext i32 %3 to i64 store i64 %4, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/call_ustack.ll000066400000000000000000000517661477746507000217620ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %"struct map_t.3" = type { ptr, ptr, ptr, ptr } %"struct map_t.4" = type { ptr, ptr, ptr, ptr } %"struct map_t.5" = type { ptr, ptr, ptr, ptr } %"struct map_t.6" = type { ptr, ptr } %"struct map_t.7" = type { ptr, ptr, ptr, ptr } %ustack_key = type { i64, i64, i32, i32 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @AT_z = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !23 @stack_perf_127 = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !25 @stack_bpftrace_6 = dso_local global %"struct map_t.3" zeroinitializer, section ".maps", !dbg !49 @stack_bpftrace_127 = dso_local global %"struct map_t.4" zeroinitializer, section ".maps", !dbg !58 @stack_scratch = dso_local global %"struct map_t.5" zeroinitializer, section ".maps", !dbg !60 @ringbuf = dso_local global %"struct map_t.6" zeroinitializer, section ".maps", !dbg !70 @event_loss_counter = dso_local global %"struct map_t.7" zeroinitializer, section ".maps", !dbg !84 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !97 { entry: %"@z_key" = alloca i64, align 8 %lookup_stack_scratch_key21 = alloca i32, align 4 %stack_key18 = alloca %ustack_key, align 8 %"@y_key" = alloca i64, align 8 %lookup_stack_scratch_key5 = alloca i32, align 4 %stack_key2 = alloca %ustack_key, align 8 %"@x_key" = alloca i64, align 8 %lookup_stack_scratch_key = alloca i32, align 4 %stack_key = alloca %ustack_key, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key) call void @llvm.memset.p0.i64(ptr align 1 %stack_key, i8 0, i64 24, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key) store i32 0, ptr %lookup_stack_scratch_key, align 4 %lookup_stack_scratch_map = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key) %lookup_stack_scratch_cond = icmp ne ptr %lookup_stack_scratch_map, null br i1 %lookup_stack_scratch_cond, label %lookup_stack_scratch_merge, label %lookup_stack_scratch_failure stack_scratch_failure: ; preds = %lookup_stack_scratch_failure br label %merge_block merge_block: ; preds = %stack_scratch_failure, %get_stack_success, %get_stack_fail %1 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 2 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %2 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %2 to i32 store i32 %pid, ptr %1, align 4 %3 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 3 store i32 0, ptr %3, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %stack_key, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key2) call void @llvm.memset.p0.i64(ptr align 1 %stack_key2, i8 0, i64 24, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key5) store i32 0, ptr %lookup_stack_scratch_key5, align 4 %lookup_stack_scratch_map6 = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key5) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key5) %lookup_stack_scratch_cond9 = icmp ne ptr %lookup_stack_scratch_map6, null br i1 %lookup_stack_scratch_cond9, label %lookup_stack_scratch_merge8, label %lookup_stack_scratch_failure7 lookup_stack_scratch_failure: ; preds = %entry br label %stack_scratch_failure lookup_stack_scratch_merge: ; preds = %entry %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map, i32 1016, ptr null) %get_stack = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map, i32 1016, i64 256) %4 = icmp sge i64 %get_stack, 0 br i1 %4, label %get_stack_success, label %get_stack_fail get_stack_success: ; preds = %lookup_stack_scratch_merge %5 = udiv i64 %get_stack, 8 %6 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 1 store i64 %5, ptr %6, align 8 %7 = trunc i64 %5 to i8 %murmur_hash_2 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map, i8 %7, i64 1) %8 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 0 store i64 %murmur_hash_2, ptr %8, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_127, ptr %stack_key, ptr %lookup_stack_scratch_map, i64 0) br label %merge_block get_stack_fail: ; preds = %lookup_stack_scratch_merge br label %merge_block stack_scratch_failure3: ; preds = %lookup_stack_scratch_failure7 br label %merge_block4 merge_block4: ; preds = %stack_scratch_failure3, %get_stack_success10, %get_stack_fail11 %9 = getelementptr %ustack_key, ptr %stack_key2, i64 0, i32 2 %get_pid_tgid15 = call i64 inttoptr (i64 14 to ptr)() %10 = lshr i64 %get_pid_tgid15, 32 %pid16 = trunc i64 %10 to i32 store i32 %pid16, ptr %9, align 4 %11 = getelementptr %ustack_key, ptr %stack_key2, i64 0, i32 3 store i32 0, ptr %11, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 %update_elem17 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %stack_key2, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key18) call void @llvm.memset.p0.i64(ptr align 1 %stack_key18, i8 0, i64 24, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key21) store i32 0, ptr %lookup_stack_scratch_key21, align 4 %lookup_stack_scratch_map22 = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key21) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key21) %lookup_stack_scratch_cond25 = icmp ne ptr %lookup_stack_scratch_map22, null br i1 %lookup_stack_scratch_cond25, label %lookup_stack_scratch_merge24, label %lookup_stack_scratch_failure23 lookup_stack_scratch_failure7: ; preds = %merge_block br label %stack_scratch_failure3 lookup_stack_scratch_merge8: ; preds = %merge_block call void @llvm.memset.p0.i64(ptr align 1 %lookup_stack_scratch_map6, i8 0, i64 48, i1 false) %get_stack12 = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map6, i32 48, i64 256) %12 = icmp sge i64 %get_stack12, 0 br i1 %12, label %get_stack_success10, label %get_stack_fail11 get_stack_success10: ; preds = %lookup_stack_scratch_merge8 %13 = udiv i64 %get_stack12, 8 %14 = getelementptr %ustack_key, ptr %stack_key2, i64 0, i32 1 store i64 %13, ptr %14, align 8 %15 = trunc i64 %13 to i8 %murmur_hash_213 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map6, i8 %15, i64 1) %16 = getelementptr %ustack_key, ptr %stack_key2, i64 0, i32 0 store i64 %murmur_hash_213, ptr %16, align 8 %update_elem14 = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_6, ptr %stack_key2, ptr %lookup_stack_scratch_map6, i64 0) br label %merge_block4 get_stack_fail11: ; preds = %lookup_stack_scratch_merge8 br label %merge_block4 stack_scratch_failure19: ; preds = %lookup_stack_scratch_failure23 br label %merge_block20 merge_block20: ; preds = %stack_scratch_failure19, %get_stack_success27, %get_stack_fail28 %17 = getelementptr %ustack_key, ptr %stack_key18, i64 0, i32 2 %get_pid_tgid32 = call i64 inttoptr (i64 14 to ptr)() %18 = lshr i64 %get_pid_tgid32, 32 %pid33 = trunc i64 %18 to i32 store i32 %pid33, ptr %17, align 4 %19 = getelementptr %ustack_key, ptr %stack_key18, i64 0, i32 3 store i32 0, ptr %19, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@z_key") store i64 0, ptr %"@z_key", align 8 %update_elem34 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_z, ptr %"@z_key", ptr %stack_key18, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@z_key") ret i64 0 lookup_stack_scratch_failure23: ; preds = %merge_block4 br label %stack_scratch_failure19 lookup_stack_scratch_merge24: ; preds = %merge_block4 %probe_read_kernel26 = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map22, i32 1016, ptr null) %get_stack29 = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map22, i32 1016, i64 256) %20 = icmp sge i64 %get_stack29, 0 br i1 %20, label %get_stack_success27, label %get_stack_fail28 get_stack_success27: ; preds = %lookup_stack_scratch_merge24 %21 = udiv i64 %get_stack29, 8 %22 = getelementptr %ustack_key, ptr %stack_key18, i64 0, i32 1 store i64 %21, ptr %22, align 8 %23 = trunc i64 %21 to i8 %murmur_hash_230 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map22, i8 %23, i64 1) %24 = getelementptr %ustack_key, ptr %stack_key18, i64 0, i32 0 store i64 %murmur_hash_230, ptr %24, align 8 %update_elem31 = call i64 inttoptr (i64 2 to ptr)(ptr @stack_perf_127, ptr %stack_key18, ptr %lookup_stack_scratch_map22, i64 0) br label %merge_block20 get_stack_fail28: ; preds = %lookup_stack_scratch_merge24 br label %merge_block20 } ; Function Attrs: alwaysinline define internal i64 @murmur_hash_2(ptr %0, i8 %1, i64 %2) #1 section "helpers" { entry: %k = alloca i64, align 8 %i = alloca i8, align 1 %id = alloca i64, align 8 %seed_addr = alloca i64, align 8 %nr_stack_frames_addr = alloca i8, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %id) call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %k) store i8 %1, ptr %nr_stack_frames_addr, align 1 store i64 %2, ptr %seed_addr, align 8 %3 = load i8, ptr %nr_stack_frames_addr, align 1 %4 = zext i8 %3 to i64 %5 = mul i64 %4, -4132994306676758123 %6 = load i64, ptr %seed_addr, align 8 %7 = xor i64 %6, %5 store i64 %7, ptr %id, align 8 store i8 0, ptr %i, align 1 br label %while_cond while_cond: ; preds = %while_body, %entry %8 = load i8, ptr %nr_stack_frames_addr, align 1 %9 = load i8, ptr %i, align 1 %length.cmp = icmp ult i8 %9, %8 br i1 %length.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %10 = load i8, ptr %i, align 1 %11 = getelementptr i64, ptr %0, i8 %10 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %k, align 8 %13 = load i64, ptr %k, align 8 %14 = mul i64 %13, -4132994306676758123 store i64 %14, ptr %k, align 8 %15 = load i64, ptr %k, align 8 %16 = lshr i64 %15, 47 %17 = load i64, ptr %k, align 8 %18 = xor i64 %17, %16 store i64 %18, ptr %k, align 8 %19 = load i64, ptr %k, align 8 %20 = mul i64 %19, -4132994306676758123 store i64 %20, ptr %k, align 8 %21 = load i64, ptr %k, align 8 %22 = load i64, ptr %id, align 8 %23 = xor i64 %22, %21 store i64 %23, ptr %id, align 8 %24 = load i64, ptr %id, align 8 %25 = mul i64 %24, -4132994306676758123 store i64 %25, ptr %id, align 8 %26 = load i8, ptr %i, align 1 %27 = add i8 %26, 1 store i8 %27, ptr %i, align 1 br label %while_cond while_end: ; preds = %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %i) call void @llvm.lifetime.end.p0(i64 -1, ptr %k) %28 = load i64, ptr %id, align 8 %zero_cond = icmp eq i64 %28, 0 br i1 %zero_cond, label %if_zero, label %if_end if_zero: ; preds = %while_end store i64 1, ptr %id, align 8 br label %if_end if_end: ; preds = %if_zero, %while_end %29 = load i64, ptr %id, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %id) ret i64 %29 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!94} !llvm.module.flags = !{!96} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 192, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 24, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !23 = !DIGlobalVariableExpression(var: !24, expr: !DIExpression()) !24 = distinct !DIGlobalVariable(name: "AT_z", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "stack_perf_127", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !28) !28 = !{!29, !34, !39, !44} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 288, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 9, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 4194304, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 131072, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 96, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 12, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !45, size: 64, offset: 192) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 8128, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 127, lowerBound: 0) !49 = !DIGlobalVariableExpression(var: !50, expr: !DIExpression()) !50 = distinct !DIGlobalVariable(name: "stack_bpftrace_6", linkageName: "global", scope: !2, file: !2, type: !51, isLocal: false, isDefinition: true) !51 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !52) !52 = !{!29, !34, !39, !53} !53 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !54, size: 64, offset: 192) !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 384, elements: !56) !56 = !{!57} !57 = !DISubrange(count: 6, lowerBound: 0) !58 = !DIGlobalVariableExpression(var: !59, expr: !DIExpression()) !59 = distinct !DIGlobalVariable(name: "stack_bpftrace_127", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !60 = !DIGlobalVariableExpression(var: !61, expr: !DIExpression()) !61 = distinct !DIGlobalVariable(name: "stack_scratch", linkageName: "global", scope: !2, file: !2, type: !62, isLocal: false, isDefinition: true) !62 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !63) !63 = !{!64, !11, !67, !44} !64 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !65, size: 64) !65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) !66 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !56) !67 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !68, size: 64, offset: 128) !68 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !69, size: 64) !69 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !70 = !DIGlobalVariableExpression(var: !71, expr: !DIExpression()) !71 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !72, isLocal: false, isDefinition: true) !72 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !73) !73 = !{!74, !79} !74 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !75, size: 64) !75 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !76, size: 64) !76 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !77) !77 = !{!78} !78 = !DISubrange(count: 27, lowerBound: 0) !79 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !80, size: 64, offset: 64) !80 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !81, size: 64) !81 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !82) !82 = !{!83} !83 = !DISubrange(count: 262144, lowerBound: 0) !84 = !DIGlobalVariableExpression(var: !85, expr: !DIExpression()) !85 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !86, isLocal: false, isDefinition: true) !86 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !87) !87 = !{!88, !11, !67, !93} !88 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !89, size: 64) !89 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !90, size: 64) !90 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !91) !91 = !{!92} !92 = !DISubrange(count: 2, lowerBound: 0) !93 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !94 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !95) !95 = !{!0, !21, !23, !25, !49, !58, !60, !70, !84} !96 = !{i32 2, !"Debug Info Version", i32 3} !97 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !98, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !94, retainedNodes: !101) !98 = !DISubroutineType(types: !99) !99 = !{!14, !100} !100 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !101 = !{!102} !102 = !DILocalVariable(name: "ctx", arg: 1, scope: !97, file: !2, type: !100) bpftrace-0.23.2/tests/codegen/llvm/call_usym_key.ll000066400000000000000000000144711477746507000223250ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %usym_t = type { i64, i32, i32 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !55 { entry: %"@x_val" = alloca i64, align 8 %usym = alloca %usym_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %usym) %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = getelementptr %usym_t, ptr %usym, i64 0, i32 0 %3 = getelementptr %usym_t, ptr %usym, i64 0, i32 1 %4 = getelementptr %usym_t, ptr %usym, i64 0, i32 2 store i64 0, ptr %2, align 8 store i32 %pid, ptr %3, align 4 store i32 0, ptr %4, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %usym, ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!52} !llvm.module.flags = !{!54} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !22} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 128, elements: !20) !19 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !20 = !{!21} !21 = !DISubrange(count: 16, lowerBound: 0) !22 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !23, size: 64, offset: 192) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !48, !49, !22} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !52 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !53) !53 = !{!0, !25, !39} !54 = !{i32 2, !"Debug Info Version", i32 3} !55 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !52, retainedNodes: !59) !56 = !DISubroutineType(types: !57) !57 = !{!24, !58} !58 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !55, file: !2, type: !58) bpftrace-0.23.2/tests/codegen/llvm/call_zero.ll000066400000000000000000000164511477746507000214370ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %zero_t = type <{ i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define i64 @kprobe_f_2(ptr %0) section "s_kprobe_f_2" !dbg !52 { entry: %key = alloca i32, align 4 %"zero_@x" = alloca %zero_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"zero_@x") %1 = getelementptr %zero_t, ptr %"zero_@x", i64 0, i32 0 store i64 30003, ptr %1, align 8 %2 = getelementptr %zero_t, ptr %"zero_@x", i64 0, i32 1 store i32 0, ptr %2, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %"zero_@x", i64 12, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %"zero_@x") ret i64 0 lookup_success: ; preds = %event_loss_counter %3 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) !52 = distinct !DISubprogram(name: "kprobe_f_2", linkageName: "kprobe_f_2", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !53) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !52, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/cast_arr_to_int.ll000066400000000000000000000140021477746507000226250ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %addr4 = alloca [4 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %addr4) %1 = getelementptr [4 x i8], ptr %addr4, i64 0, i64 0 store i8 127, ptr %1, align 1 %2 = getelementptr [4 x i8], ptr %addr4, i64 0, i64 1 store i8 0, ptr %2, align 1 %3 = getelementptr [4 x i8], ptr %addr4, i64 0, i64 2 store i8 0, ptr %3, align 1 %4 = getelementptr [4 x i8], ptr %addr4, i64 0, i64 3 store i8 1, ptr %4, align 1 %5 = load volatile i32, ptr %addr4, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %addr4) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") %6 = zext i32 %5 to i64 store i64 %6, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/cast_int_to_arr.ll000066400000000000000000000136541477746507000226410ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %"$a" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$a") store i64 0, ptr %"$a", align 8 %1 = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %1) store i64 0, ptr %1, align 8 %2 = ptrtoint ptr %1 to i64 store i64 %2, ptr %"$a", align 8 %3 = load i64, ptr %"$a", align 8 %4 = inttoptr i64 %3 to ptr %5 = getelementptr [8 x i8], ptr %4, i32 0, i64 0 %6 = load volatile i8, ptr %5, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") %7 = zext i8 %6 to i64 store i64 %7, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/comparison_extend.ll000066400000000000000000000136011477746507000232000ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = icmp ult i64 1, %arg0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") %4 = zext i1 %3 to i64 store i64 %4, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/count_cast.ll000066400000000000000000000217661477746507000216340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !22 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !36 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !45 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %"@x_key1" = alloca i64, align 8 %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 %2 = add i64 %1, 1 store i64 %2, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %lookup_success2, %lookup_merge %3 = load i32, ptr @num_cpus, align 4 %4 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %4, %3 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %5 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %"@x_key1", i32 %5) %map_lookup_cond4 = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond4, label %lookup_success2, label %lookup_failure3 while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %6 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") store i64 %6, ptr %"$res", align 8 ret i64 0 lookup_success2: ; preds = %while_body %7 = load i64, ptr %val_1, align 8 %8 = load i64, ptr %lookup_percpu_elem, align 8 %9 = add i64 %8, %7 store i64 %9, ptr %val_1, align 8 %10 = load i32, ptr %i, align 4 %11 = add i32 %10, 1 store i32 %11, ptr %i, align 4 br label %while_cond lookup_failure3: ; preds = %while_body %12 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %12, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure error_success: ; preds = %lookup_failure3 br label %while_end error_failure: ; preds = %lookup_failure3 %13 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 6, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !22 = !DIGlobalVariableExpression(var: !23, expr: !DIExpression()) !23 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !24 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !25) !25 = !{!26, !31} !26 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !27, size: 64) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !29) !29 = !{!30} !30 = !DISubrange(count: 27, lowerBound: 0) !31 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !32, size: 64, offset: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 262144, lowerBound: 0) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !38, isLocal: false, isDefinition: true) !38 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !39) !39 = !{!40, !11, !16, !19} !40 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !41, size: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 2, lowerBound: 0) !45 = !DIGlobalVariableExpression(var: !46, expr: !DIExpression()) !46 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !21, isLocal: false, isDefinition: true) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !22, !36, !45} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!21, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/count_cast_loop.ll000066400000000000000000000255731477746507000226650ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %int64_count_t__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !51 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !56 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 %2 = add i64 %1, 1 store i64 %2, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_x, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !63 { %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %"$kv" = alloca %int64_count_t__tuple_t, align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %key = load i64, ptr %1, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %lookup_success, %4 %5 = load i32, ptr @num_cpus, align 4 %6 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %6, %5 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %7 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %1, i32 %7) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %8 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %9 = getelementptr %int64_count_t__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %9, align 8 %10 = getelementptr %int64_count_t__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %8, ptr %10, align 8 %11 = getelementptr %int64_count_t__tuple_t, ptr %"$kv", i32 0, i32 1 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %"$res", align 8 ret i64 0 lookup_success: ; preds = %while_body %13 = load i64, ptr %val_1, align 8 %14 = load i64, ptr %lookup_percpu_elem, align 8 %15 = add i64 %14, %13 store i64 %15, ptr %val_1, align 8 %16 = load i32, ptr %i, align 4 %17 = add i32 %16, 1 store i32 %17, ptr %i, align 4 br label %while_cond lookup_failure: ; preds = %while_body %18 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %18, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure error_success: ; preds = %lookup_failure br label %while_end error_failure: ; preds = %lookup_failure %19 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!53} !llvm.module.flags = !{!55} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !48, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !44, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 1, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !49, size: 64, offset: 128) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !51 = !DIGlobalVariableExpression(var: !52, expr: !DIExpression()) !52 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !53 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !54) !54 = !{!0, !20, !34, !51} !55 = !{i32 2, !"Debug Info Version", i32 3} !56 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !57, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !53, retainedNodes: !61) !57 = !DISubroutineType(types: !58) !58 = !{!18, !59} !59 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !60, size: 64) !60 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !56, file: !2, type: !59) !63 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !64, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !53, retainedNodes: !66) !64 = !DISubroutineType(types: !65) !65 = !{!18, !59, !59, !59, !59} !66 = !{!67, !68, !69, !70} !67 = !DILocalVariable(name: "map", arg: 1, scope: !63, file: !2, type: !59) !68 = !DILocalVariable(name: "key", arg: 2, scope: !63, file: !2, type: !59) !69 = !DILocalVariable(name: "value", arg: 3, scope: !63, file: !2, type: !59) !70 = !DILocalVariable(name: "ctx", arg: 4, scope: !63, file: !2, type: !59) bpftrace-0.23.2/tests/codegen/llvm/count_cast_loop_multi_key.ll000066400000000000000000000276131477746507000247440ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %int64_int64__tuple_t = type { i64, i64 } %"(int64,int64)_count_t__tuple_t" = type { %int64_int64__tuple_t, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !56 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !61 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %tuple = alloca %int64_int64__tuple_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 16, i1 false) %1 = getelementptr %int64_int64__tuple_t, ptr %tuple, i32 0, i32 0 store i64 1, ptr %1, align 8 %2 = getelementptr %int64_int64__tuple_t, ptr %tuple, i32 0, i32 1 store i64 2, ptr %2, align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %tuple) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %3 = load i64, ptr %lookup_elem, align 8 %4 = add i64 %3, 1 store i64 %4, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %tuple, ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_x, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !68 { %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %"$kv" = alloca %"(int64,int64)_count_t__tuple_t", align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %lookup_success, %4 %5 = load i32, ptr @num_cpus, align 4 %6 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %6, %5 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %7 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %1, i32 %7) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %8 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 24, i1 false) %9 = getelementptr %"(int64,int64)_count_t__tuple_t", ptr %"$kv", i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %9, ptr align 1 %1, i64 16, i1 false) %10 = getelementptr %"(int64,int64)_count_t__tuple_t", ptr %"$kv", i32 0, i32 1 store i64 %8, ptr %10, align 8 %11 = getelementptr %"(int64,int64)_count_t__tuple_t", ptr %"$kv", i32 0, i32 1 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %"$res", align 8 ret i64 0 lookup_success: ; preds = %while_body %13 = load i64, ptr %val_1, align 8 %14 = load i64, ptr %lookup_percpu_elem, align 8 %15 = add i64 %14, %13 store i64 %15, ptr %val_1, align 8 %16 = load i32, ptr %i, align 4 %17 = add i32 %16, 1 store i32 %17, ptr %i, align 4 br label %while_cond lookup_failure: ; preds = %while_body %18 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %18, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure error_success: ; preds = %lookup_failure br label %while_end error_failure: ; preds = %lookup_failure %19 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!58} !llvm.module.flags = !{!60} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !23} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !22} !20 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !22 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 64, offset: 64) !23 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !24, size: 64, offset: 192) !24 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !48, !53, !23} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !49, size: 64, offset: 64) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !51) !51 = !{!52} !52 = !DISubrange(count: 1, lowerBound: 0) !53 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !54, size: 64, offset: 128) !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !56 = !DIGlobalVariableExpression(var: !57, expr: !DIExpression()) !57 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !21, isLocal: false, isDefinition: true) !58 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !59) !59 = !{!0, !25, !39, !56} !60 = !{i32 2, !"Debug Info Version", i32 3} !61 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !62, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !58, retainedNodes: !66) !62 = !DISubroutineType(types: !63) !63 = !{!21, !64} !64 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !65, size: 64) !65 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !66 = !{!67} !67 = !DILocalVariable(name: "ctx", arg: 1, scope: !61, file: !2, type: !64) !68 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !69, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !58, retainedNodes: !71) !69 = !DISubroutineType(types: !70) !70 = !{!21, !64, !64, !64, !64} !71 = !{!72, !73, !74, !75} !72 = !DILocalVariable(name: "map", arg: 1, scope: !68, file: !2, type: !64) !73 = !DILocalVariable(name: "key", arg: 2, scope: !68, file: !2, type: !64) !74 = !DILocalVariable(name: "value", arg: 3, scope: !68, file: !2, type: !64) !75 = !DILocalVariable(name: "ctx", arg: 4, scope: !68, file: !2, type: !64) bpftrace-0.23.2/tests/codegen/llvm/count_cast_loop_stack_key.ll000066400000000000000000000453601477746507000247160ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct map_t.2" = type { ptr, ptr } %"struct map_t.3" = type { ptr, ptr, ptr, ptr } %kstack_key = type { i64, i64 } %kstack_count_t__tuple_t = type { %kstack_key, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @stack_raw_127 = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @stack_scratch = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !49 @ringbuf = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !66 @event_loss_counter = dso_local global %"struct map_t.3" zeroinitializer, section ".maps", !dbg !80 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !89 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !94 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %lookup_stack_scratch_key = alloca i32, align 4 %stack_key = alloca %kstack_key, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key) call void @llvm.memset.p0.i64(ptr align 1 %stack_key, i8 0, i64 16, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key) store i32 0, ptr %lookup_stack_scratch_key, align 4 %lookup_stack_scratch_map = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key) %lookup_stack_scratch_cond = icmp ne ptr %lookup_stack_scratch_map, null br i1 %lookup_stack_scratch_cond, label %lookup_stack_scratch_merge, label %lookup_stack_scratch_failure stack_scratch_failure: ; preds = %lookup_stack_scratch_failure br label %merge_block merge_block: ; preds = %stack_scratch_failure, %get_stack_success, %get_stack_fail %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %stack_key) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_stack_scratch_failure: ; preds = %entry br label %stack_scratch_failure lookup_stack_scratch_merge: ; preds = %entry %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map, i32 1016, ptr null) %get_stack = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map, i32 1016, i64 0) %1 = icmp sge i64 %get_stack, 0 br i1 %1, label %get_stack_success, label %get_stack_fail get_stack_success: ; preds = %lookup_stack_scratch_merge %2 = udiv i64 %get_stack, 8 %3 = getelementptr %kstack_key, ptr %stack_key, i64 0, i32 1 store i64 %2, ptr %3, align 8 %4 = trunc i64 %2 to i8 %murmur_hash_2 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map, i8 %4, i64 1) %5 = getelementptr %kstack_key, ptr %stack_key, i64 0, i32 0 store i64 %murmur_hash_2, ptr %5, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @stack_raw_127, ptr %stack_key, ptr %lookup_stack_scratch_map, i64 0) br label %merge_block get_stack_fail: ; preds = %lookup_stack_scratch_merge br label %merge_block lookup_success: ; preds = %merge_block %6 = load i64, ptr %lookup_elem, align 8 %7 = add i64 %6, 1 store i64 %7, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %merge_block call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %stack_key, ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_x, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 } ; Function Attrs: alwaysinline define internal i64 @murmur_hash_2(ptr %0, i8 %1, i64 %2) #1 section "helpers" { entry: %k = alloca i64, align 8 %i = alloca i8, align 1 %id = alloca i64, align 8 %seed_addr = alloca i64, align 8 %nr_stack_frames_addr = alloca i8, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %id) call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %k) store i8 %1, ptr %nr_stack_frames_addr, align 1 store i64 %2, ptr %seed_addr, align 8 %3 = load i8, ptr %nr_stack_frames_addr, align 1 %4 = zext i8 %3 to i64 %5 = mul i64 %4, -4132994306676758123 %6 = load i64, ptr %seed_addr, align 8 %7 = xor i64 %6, %5 store i64 %7, ptr %id, align 8 store i8 0, ptr %i, align 1 br label %while_cond while_cond: ; preds = %while_body, %entry %8 = load i8, ptr %nr_stack_frames_addr, align 1 %9 = load i8, ptr %i, align 1 %length.cmp = icmp ult i8 %9, %8 br i1 %length.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %10 = load i8, ptr %i, align 1 %11 = getelementptr i64, ptr %0, i8 %10 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %k, align 8 %13 = load i64, ptr %k, align 8 %14 = mul i64 %13, -4132994306676758123 store i64 %14, ptr %k, align 8 %15 = load i64, ptr %k, align 8 %16 = lshr i64 %15, 47 %17 = load i64, ptr %k, align 8 %18 = xor i64 %17, %16 store i64 %18, ptr %k, align 8 %19 = load i64, ptr %k, align 8 %20 = mul i64 %19, -4132994306676758123 store i64 %20, ptr %k, align 8 %21 = load i64, ptr %k, align 8 %22 = load i64, ptr %id, align 8 %23 = xor i64 %22, %21 store i64 %23, ptr %id, align 8 %24 = load i64, ptr %id, align 8 %25 = mul i64 %24, -4132994306676758123 store i64 %25, ptr %id, align 8 %26 = load i8, ptr %i, align 1 %27 = add i8 %26, 1 store i8 %27, ptr %i, align 1 br label %while_cond while_end: ; preds = %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %i) call void @llvm.lifetime.end.p0(i64 -1, ptr %k) %28 = load i64, ptr %id, align 8 %zero_cond = icmp eq i64 %28, 0 br i1 %zero_cond, label %if_zero, label %if_end if_zero: ; preds = %while_end store i64 1, ptr %id, align 8 br label %if_end if_end: ; preds = %if_zero, %while_end %29 = load i64, ptr %id, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %id) ret i64 %29 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !100 { %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %"$kv" = alloca %kstack_count_t__tuple_t, align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %lookup_success, %4 %5 = load i32, ptr @num_cpus, align 4 %6 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %6, %5 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %7 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %1, i32 %7) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %8 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 24, i1 false) %9 = getelementptr %kstack_count_t__tuple_t, ptr %"$kv", i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %9, ptr align 1 %1, i64 16, i1 false) %10 = getelementptr %kstack_count_t__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %8, ptr %10, align 8 %11 = getelementptr %kstack_count_t__tuple_t, ptr %"$kv", i32 0, i32 1 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %"$res", align 8 ret i64 0 lookup_success: ; preds = %while_body %13 = load i64, ptr %val_1, align 8 %14 = load i64, ptr %lookup_percpu_elem, align 8 %15 = add i64 %14, %13 store i64 %15, ptr %val_1, align 8 %16 = load i32, ptr %i, align 4 %17 = add i32 %16, 1 store i32 %17, ptr %i, align 4 br label %while_cond lookup_failure: ; preds = %while_body %18 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %18, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure error_success: ; preds = %lookup_failure br label %while_end error_failure: ; preds = %lookup_failure %19 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #4 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!91} !llvm.module.flags = !{!93} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !22} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 128, elements: !20) !19 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !20 = !{!21} !21 = !DISubrange(count: 16, lowerBound: 0) !22 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !23, size: 64, offset: 192) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "stack_raw_127", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !28) !28 = !{!29, !34, !39, !44} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 288, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 9, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 4194304, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 131072, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 96, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 12, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !45, size: 64, offset: 192) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !24, size: 8128, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 127, lowerBound: 0) !49 = !DIGlobalVariableExpression(var: !50, expr: !DIExpression()) !50 = distinct !DIGlobalVariable(name: "stack_scratch", linkageName: "global", scope: !2, file: !2, type: !51, isLocal: false, isDefinition: true) !51 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !52) !52 = !{!53, !58, !63, !44} !53 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !54, size: 64) !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !56) !56 = !{!57} !57 = !DISubrange(count: 6, lowerBound: 0) !58 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !59, size: 64, offset: 64) !59 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !60, size: 64) !60 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !61) !61 = !{!62} !62 = !DISubrange(count: 1, lowerBound: 0) !63 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !64, size: 64, offset: 128) !64 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !65, size: 64) !65 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !66 = !DIGlobalVariableExpression(var: !67, expr: !DIExpression()) !67 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !68, isLocal: false, isDefinition: true) !68 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !69) !69 = !{!70, !75} !70 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !71, size: 64) !71 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !72, size: 64) !72 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !73) !73 = !{!74} !74 = !DISubrange(count: 27, lowerBound: 0) !75 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !76, size: 64, offset: 64) !76 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !77, size: 64) !77 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !78) !78 = !{!79} !79 = !DISubrange(count: 262144, lowerBound: 0) !80 = !DIGlobalVariableExpression(var: !81, expr: !DIExpression()) !81 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !82, isLocal: false, isDefinition: true) !82 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !83) !83 = !{!84, !58, !63, !22} !84 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !85, size: 64) !85 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !86, size: 64) !86 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !87) !87 = !{!88} !88 = !DISubrange(count: 2, lowerBound: 0) !89 = !DIGlobalVariableExpression(var: !90, expr: !DIExpression()) !90 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !91 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !92) !92 = !{!0, !25, !49, !66, !80, !89} !93 = !{i32 2, !"Debug Info Version", i32 3} !94 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !95, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !91, retainedNodes: !98) !95 = !DISubroutineType(types: !96) !96 = !{!24, !97} !97 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) !98 = !{!99} !99 = !DILocalVariable(name: "ctx", arg: 1, scope: !94, file: !2, type: !97) !100 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !101, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !91, retainedNodes: !103) !101 = !DISubroutineType(types: !102) !102 = !{!24, !97, !97, !97, !97} !103 = !{!104, !105, !106, !107} !104 = !DILocalVariable(name: "map", arg: 1, scope: !100, file: !2, type: !97) !105 = !DILocalVariable(name: "key", arg: 2, scope: !100, file: !2, type: !97) !106 = !DILocalVariable(name: "value", arg: 3, scope: !100, file: !2, type: !97) !107 = !DILocalVariable(name: "ctx", arg: 4, scope: !100, file: !2, type: !97) bpftrace-0.23.2/tests/codegen/llvm/count_no_cast_for_print.ll000066400000000000000000000204741477746507000244050ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %print_t = type <{ i64, i32, i32, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !22 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !36 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !45 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !50 { entry: %key = alloca i32, align 4 %"print_@" = alloca %print_t, align 8 %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 %2 = add i64 %1, 1 store i64 %2, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"print_@") %3 = getelementptr %print_t, ptr %"print_@", i64 0, i32 0 store i64 30001, ptr %3, align 8 %4 = getelementptr %print_t, ptr %"print_@", i64 0, i32 1 store i32 0, ptr %4, align 4 %5 = getelementptr %print_t, ptr %"print_@", i64 0, i32 2 store i32 0, ptr %5, align 4 %6 = getelementptr %print_t, ptr %"print_@", i64 0, i32 3 store i32 0, ptr %6, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %"print_@", i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %lookup_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem1 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond5 = icmp ne ptr %lookup_elem1, null br i1 %map_lookup_cond5, label %lookup_success2, label %lookup_failure3 counter_merge: ; preds = %lookup_merge4, %lookup_merge call void @llvm.lifetime.end.p0(i64 -1, ptr %"print_@") ret i64 0 lookup_success2: ; preds = %event_loss_counter %7 = atomicrmw add ptr %lookup_elem1, i64 1 seq_cst, align 8 br label %lookup_merge4 lookup_failure3: ; preds = %event_loss_counter br label %lookup_merge4 lookup_merge4: ; preds = %lookup_failure3, %lookup_success2 call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 6, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !22 = !DIGlobalVariableExpression(var: !23, expr: !DIExpression()) !23 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !24 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !25) !25 = !{!26, !31} !26 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !27, size: 64) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !29) !29 = !{!30} !30 = !DISubrange(count: 27, lowerBound: 0) !31 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !32, size: 64, offset: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 262144, lowerBound: 0) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !38, isLocal: false, isDefinition: true) !38 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !39) !39 = !{!40, !11, !16, !19} !40 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !41, size: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 2, lowerBound: 0) !45 = !DIGlobalVariableExpression(var: !46, expr: !DIExpression()) !46 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !21, isLocal: false, isDefinition: true) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !22, !36, !45} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!21, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/dereference.ll000066400000000000000000000133441477746507000217320ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %deref = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %deref) %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %deref, i32 8, i64 1234) %1 = load i64, ptr %deref, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %deref) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/empty_function.ll000066400000000000000000000076661477746507000225400ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/enum_declaration.ll000066400000000000000000000144161477746507000227750ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_a = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_b = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %"@b_val" = alloca i64, align 8 %"@b_key" = alloca i64, align 8 %"@a_val" = alloca i64, align 8 %"@a_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@a_key") store i64 0, ptr %"@a_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@a_val") store i64 42, ptr %"@a_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_a, ptr %"@a_key", ptr %"@a_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@a_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@a_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@b_key") store i64 0, ptr %"@b_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@b_val") store i64 43, ptr %"@b_val", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_b, ptr %"@b_key", ptr %"@b_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@b_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@b_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_a", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_b", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/fentry_dereference.ll000066400000000000000000000150241477746507000233160ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @fentry_mock_vmlinux_tcp_sendmsg_1(ptr %0) section "s_fentry_mock_vmlinux_tcp_sendmsg_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 0 %sk = load volatile i64, ptr %2, align 8 %3 = inttoptr i64 %sk to ptr %4 = call ptr @llvm.preserve.static.offset(ptr %3) %5 = getelementptr i8, ptr %4, i64 0 %6 = call ptr @llvm.preserve.static.offset(ptr %5) %7 = getelementptr i8, ptr %6, i64 0 %8 = load volatile i32, ptr %7, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") %9 = zext i32 %8 to i64 store i64 %9, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "fentry_mock_vmlinux_tcp_sendmsg_1", linkageName: "fentry_mock_vmlinux_tcp_sendmsg_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/fentry_recursion_check.ll000066400000000000000000000251671477746507000242260ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @recursion_prevention = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !22 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !36 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @fentry_mock_vmlinux_queued_spin_lock_slowpath_1(ptr %0) section "s_fentry_mock_vmlinux_queued_spin_lock_slowpath_1" !dbg !48 { entry: %lookup_key6 = alloca i32, align 4 %key = alloca i32, align 4 %lookup_key = alloca i32, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_key) store i32 0, ptr %lookup_key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @recursion_prevention, ptr %lookup_key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_key) %cast = ptrtoint ptr %lookup_elem to i64 %1 = atomicrmw xchg i64 %cast, i64 1 seq_cst, align 8 %value_set_condition = icmp eq i64 %1, 0 br i1 %value_set_condition, label %lookup_merge, label %value_is_set lookup_failure: ; preds = %entry ret i64 0 lookup_merge: ; preds = %lookup_success call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_key6) store i32 0, ptr %lookup_key6, align 4 %lookup_elem7 = call ptr inttoptr (i64 1 to ptr)(ptr @recursion_prevention, ptr %lookup_key6) %map_lookup_cond11 = icmp ne ptr %lookup_elem7, null br i1 %map_lookup_cond11, label %lookup_success8, label %lookup_failure9 value_is_set: ; preds = %lookup_success call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem1 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond5 = icmp ne ptr %lookup_elem1, null br i1 %map_lookup_cond5, label %lookup_success2, label %lookup_failure3 lookup_success2: ; preds = %value_is_set %2 = atomicrmw add ptr %lookup_elem1, i64 1 seq_cst, align 8 br label %lookup_merge4 lookup_failure3: ; preds = %value_is_set br label %lookup_merge4 lookup_merge4: ; preds = %lookup_failure3, %lookup_success2 call void @llvm.lifetime.end.p0(i64 -1, ptr %key) ret i64 0 lookup_success8: ; preds = %lookup_merge call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_key6) %cast12 = ptrtoint ptr %lookup_elem7 to i64 store i64 0, i64 %cast12, align 8 br label %lookup_merge10 lookup_failure9: ; preds = %lookup_merge br label %lookup_merge10 lookup_merge10: ; preds = %lookup_failure9, %lookup_success8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define i64 @tracepoint_exceptions_page_fault_user_2(ptr %0) section "s_tracepoint_exceptions_page_fault_user_2" !dbg !55 { entry: %lookup_key6 = alloca i32, align 4 %key = alloca i32, align 4 %lookup_key = alloca i32, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_key) store i32 0, ptr %lookup_key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @recursion_prevention, ptr %lookup_key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_key) %cast = ptrtoint ptr %lookup_elem to i64 %1 = atomicrmw xchg i64 %cast, i64 1 seq_cst, align 8 %value_set_condition = icmp eq i64 %1, 0 br i1 %value_set_condition, label %lookup_merge, label %value_is_set lookup_failure: ; preds = %entry ret i64 0 lookup_merge: ; preds = %lookup_success call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_key6) store i32 0, ptr %lookup_key6, align 4 %lookup_elem7 = call ptr inttoptr (i64 1 to ptr)(ptr @recursion_prevention, ptr %lookup_key6) %map_lookup_cond11 = icmp ne ptr %lookup_elem7, null br i1 %map_lookup_cond11, label %lookup_success8, label %lookup_failure9 value_is_set: ; preds = %lookup_success call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem1 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond5 = icmp ne ptr %lookup_elem1, null br i1 %map_lookup_cond5, label %lookup_success2, label %lookup_failure3 lookup_success2: ; preds = %value_is_set %2 = atomicrmw add ptr %lookup_elem1, i64 1 seq_cst, align 8 br label %lookup_merge4 lookup_failure3: ; preds = %value_is_set br label %lookup_merge4 lookup_merge4: ; preds = %lookup_failure3, %lookup_success2 call void @llvm.lifetime.end.p0(i64 -1, ptr %key) ret i64 1 lookup_success8: ; preds = %lookup_merge call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_key6) %cast12 = ptrtoint ptr %lookup_elem7 to i64 store i64 0, i64 %cast12, align 8 br label %lookup_merge10 lookup_failure9: ; preds = %lookup_merge br label %lookup_merge10 lookup_merge10: ; preds = %lookup_failure9, %lookup_success8 ret i64 1 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!45} !llvm.module.flags = !{!47} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "recursion_prevention", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 6, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !22 = !DIGlobalVariableExpression(var: !23, expr: !DIExpression()) !23 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !24 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !25) !25 = !{!26, !31} !26 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !27, size: 64) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !29) !29 = !{!30} !30 = !DISubrange(count: 27, lowerBound: 0) !31 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !32, size: 64, offset: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 262144, lowerBound: 0) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !38, isLocal: false, isDefinition: true) !38 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !39) !39 = !{!40, !11, !16, !19} !40 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !41, size: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 2, lowerBound: 0) !45 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !46) !46 = !{!0, !22, !36} !47 = !{i32 2, !"Debug Info Version", i32 3} !48 = distinct !DISubprogram(name: "fentry_mock_vmlinux_queued_spin_lock_slowpath_1", linkageName: "fentry_mock_vmlinux_queued_spin_lock_slowpath_1", scope: !2, file: !2, type: !49, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !45, retainedNodes: !53) !49 = !DISubroutineType(types: !50) !50 = !{!21, !51} !51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !52, size: 64) !52 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !48, file: !2, type: !51) !55 = distinct !DISubprogram(name: "tracepoint_exceptions_page_fault_user_2", linkageName: "tracepoint_exceptions_page_fault_user_2", scope: !2, file: !2, type: !49, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !45, retainedNodes: !56) !56 = !{!57} !57 = !DILocalVariable(name: "ctx", arg: 1, scope: !55, file: !2, type: !51) bpftrace-0.23.2/tests/codegen/llvm/fentry_recursion_check_with_predicate.ll000066400000000000000000000216771477746507000273030ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @recursion_prevention = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !22 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !36 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @fentry_mock_vmlinux_queued_spin_lock_slowpath_1(ptr %0) section "s_fentry_mock_vmlinux_queued_spin_lock_slowpath_1" !dbg !48 { entry: %lookup_key13 = alloca i32, align 4 %lookup_key6 = alloca i32, align 4 %key = alloca i32, align 4 %lookup_key = alloca i32, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_key) store i32 0, ptr %lookup_key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @recursion_prevention, ptr %lookup_key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_key) %cast = ptrtoint ptr %lookup_elem to i64 %1 = atomicrmw xchg i64 %cast, i64 1 seq_cst, align 8 %value_set_condition = icmp eq i64 %1, 0 br i1 %value_set_condition, label %lookup_merge, label %value_is_set lookup_failure: ; preds = %entry ret i64 0 lookup_merge: ; preds = %lookup_success %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %2 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %2 to i32 %3 = zext i32 %pid to i64 %4 = icmp eq i64 %3, 1234 %5 = zext i1 %4 to i64 %predcond = icmp eq i64 %5, 0 br i1 %predcond, label %pred_false, label %pred_true value_is_set: ; preds = %lookup_success call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem1 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond5 = icmp ne ptr %lookup_elem1, null br i1 %map_lookup_cond5, label %lookup_success2, label %lookup_failure3 lookup_success2: ; preds = %value_is_set %6 = atomicrmw add ptr %lookup_elem1, i64 1 seq_cst, align 8 br label %lookup_merge4 lookup_failure3: ; preds = %value_is_set br label %lookup_merge4 lookup_merge4: ; preds = %lookup_failure3, %lookup_success2 call void @llvm.lifetime.end.p0(i64 -1, ptr %key) ret i64 0 pred_false: ; preds = %lookup_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_key6) store i32 0, ptr %lookup_key6, align 4 %lookup_elem7 = call ptr inttoptr (i64 1 to ptr)(ptr @recursion_prevention, ptr %lookup_key6) %map_lookup_cond11 = icmp ne ptr %lookup_elem7, null br i1 %map_lookup_cond11, label %lookup_success8, label %lookup_failure9 pred_true: ; preds = %lookup_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_key13) store i32 0, ptr %lookup_key13, align 4 %lookup_elem14 = call ptr inttoptr (i64 1 to ptr)(ptr @recursion_prevention, ptr %lookup_key13) %map_lookup_cond18 = icmp ne ptr %lookup_elem14, null br i1 %map_lookup_cond18, label %lookup_success15, label %lookup_failure16 lookup_success8: ; preds = %pred_false call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_key6) %cast12 = ptrtoint ptr %lookup_elem7 to i64 store i64 0, i64 %cast12, align 8 br label %lookup_merge10 lookup_failure9: ; preds = %pred_false br label %lookup_merge10 lookup_merge10: ; preds = %lookup_failure9, %lookup_success8 ret i64 0 lookup_success15: ; preds = %pred_true call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_key13) %cast19 = ptrtoint ptr %lookup_elem14 to i64 store i64 0, i64 %cast19, align 8 br label %lookup_merge17 lookup_failure16: ; preds = %pred_true br label %lookup_merge17 lookup_merge17: ; preds = %lookup_failure16, %lookup_success15 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!45} !llvm.module.flags = !{!47} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "recursion_prevention", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 6, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !22 = !DIGlobalVariableExpression(var: !23, expr: !DIExpression()) !23 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !24 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !25) !25 = !{!26, !31} !26 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !27, size: 64) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !29) !29 = !{!30} !30 = !DISubrange(count: 27, lowerBound: 0) !31 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !32, size: 64, offset: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 262144, lowerBound: 0) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !38, isLocal: false, isDefinition: true) !38 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !39) !39 = !{!40, !11, !16, !19} !40 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !41, size: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 2, lowerBound: 0) !45 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !46) !46 = !{!0, !22, !36} !47 = !{i32 2, !"Debug Info Version", i32 3} !48 = distinct !DISubprogram(name: "fentry_mock_vmlinux_queued_spin_lock_slowpath_1", linkageName: "fentry_mock_vmlinux_queued_spin_lock_slowpath_1", scope: !2, file: !2, type: !49, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !45, retainedNodes: !53) !49 = !DISubroutineType(types: !50) !50 = !{!21, !51} !51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !52, size: 64) !52 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !48, file: !2, type: !51) bpftrace-0.23.2/tests/codegen/llvm/fexit_dereference.ll000066400000000000000000000150141477746507000231250ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @fexit_mock_vmlinux_sk_alloc_1(ptr %0) section "s_fexit_mock_vmlinux_sk_alloc_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 5 %retval = load volatile i64, ptr %2, align 8 %3 = inttoptr i64 %retval to ptr %4 = call ptr @llvm.preserve.static.offset(ptr %3) %5 = getelementptr i8, ptr %4, i64 0 %6 = call ptr @llvm.preserve.static.offset(ptr %5) %7 = getelementptr i8, ptr %6, i64 0 %8 = load volatile i32, ptr %7, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") %9 = zext i32 %8 to i64 store i64 %9, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "fexit_mock_vmlinux_sk_alloc_1", linkageName: "fexit_mock_vmlinux_sk_alloc_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/fmt_str_args_scratch_buf.ll000066400000000000000000000172001477746507000245130ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %printf_t = type { i64, [5 x i8], i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @fmt_str_buf = dso_local externally_initialized global [1 x [1 x [24 x i8]]] zeroinitializer, section ".data.fmt_str_buf", !dbg !38 @xxxx = global [5 x i8] c"xxxx\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !49 { entry: %key = alloca i32, align 4 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [24 x i8]]], ptr @fmt_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 call void @llvm.memset.p0.i64(ptr align 1 %2, i8 0, i64 24, i1 false) %3 = getelementptr %printf_t, ptr %2, i32 0, i32 0 store i64 0, ptr %3, align 8 %4 = getelementptr %printf_t, ptr %2, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %4, ptr align 1 @xxxx, i64 5, i1 false) %5 = getelementptr %printf_t, ptr %2, i32 0, i32 2 store i64 1, ptr %5, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %2, i64 24, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry ret i64 0 lookup_success: ; preds = %event_loss_counter %6 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #3 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "fmt_str_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 192, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 192, elements: !28) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 192, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 24, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/fmt_str_args_stack.ll000066400000000000000000000152101477746507000233340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %printf_t = type { i64, [5 x i8], i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @xxxx = global [5 x i8] c"xxxx\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %printf_args = alloca %printf_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %printf_args) call void @llvm.memset.p0.i64(ptr align 1 %printf_args, i8 0, i64 24, i1 false) %1 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 0 store i64 0, ptr %1, align 8 %2 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %2, ptr align 1 @xxxx, i64 5, i1 false) %3 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 2 store i64 1, ptr %3, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %printf_args, i64 24, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %printf_args) ret i64 0 lookup_success: ; preds = %event_loss_counter %4 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/for_map_one_key.ll000066400000000000000000000211341477746507000226130ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %int64_int64__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_map = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_x = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !31 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !45 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !60 { entry: %"@map_val" = alloca i64, align 8 %"@map_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_key") store i64 16, ptr %"@map_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_val") store i64 32, ptr %"@map_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_map, ptr %"@map_key", ptr %"@map_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_key") %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_map, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !67 { %"@x_key" = alloca i64, align 8 %"$kv" = alloca %int64_int64__tuple_t, align 8 %key = load i64, ptr %1, align 8 %val = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %5 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %5, align 8 %6 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %val, ptr %6, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"$kv", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!57} !llvm.module.flags = !{!59} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_map", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !23) !23 = !{!5, !24, !16, !25} !24 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !25 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !26, size: 64, offset: 192) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !30} !29 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !30 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64, offset: 64) !31 = !DIGlobalVariableExpression(var: !32, expr: !DIExpression()) !32 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !33, isLocal: false, isDefinition: true) !33 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !34) !34 = !{!35, !40} !35 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !36, size: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 27, lowerBound: 0) !40 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !41, size: 64, offset: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 262144, lowerBound: 0) !45 = !DIGlobalVariableExpression(var: !46, expr: !DIExpression()) !46 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !47, isLocal: false, isDefinition: true) !47 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !48) !48 = !{!49, !24, !54, !19} !49 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !50, size: 64) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !52) !52 = !{!53} !53 = !DISubrange(count: 2, lowerBound: 0) !54 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !55, size: 64, offset: 128) !55 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !56, size: 64) !56 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !57 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !58) !58 = !{!0, !20, !31, !45} !59 = !{i32 2, !"Debug Info Version", i32 3} !60 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !57, retainedNodes: !65) !61 = !DISubroutineType(types: !62) !62 = !{!18, !63} !63 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !64, size: 64) !64 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !65 = !{!66} !66 = !DILocalVariable(name: "ctx", arg: 1, scope: !60, file: !2, type: !63) !67 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !57, retainedNodes: !70) !68 = !DISubroutineType(types: !69) !69 = !{!18, !63, !63, !63, !63} !70 = !{!71, !72, !73, !74} !71 = !DILocalVariable(name: "map", arg: 1, scope: !67, file: !2, type: !63) !72 = !DILocalVariable(name: "key", arg: 2, scope: !67, file: !2, type: !63) !73 = !DILocalVariable(name: "value", arg: 3, scope: !67, file: !2, type: !63) !74 = !DILocalVariable(name: "ctx", arg: 4, scope: !67, file: !2, type: !63) bpftrace-0.23.2/tests/codegen/llvm/for_map_strings.ll000066400000000000000000000220721477746507000226550ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %"string[4]_string[4]__tuple_t" = type { [4 x i8], [4 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_map = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_x = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !23 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !37 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !51 @xyz = global [4 x i8] c"xyz\00" @abc = global [4 x i8] c"abc\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !67 { entry: %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_map, ptr @abc, ptr @xyz, i64 0) %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_map, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 } define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !73 { %"@x_key" = alloca i64, align 8 %"$kv" = alloca %"string[4]_string[4]__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 8, i1 false) %5 = getelementptr %"string[4]_string[4]__tuple_t", ptr %"$kv", i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %5, ptr align 1 %1, i64 4, i1 false) %6 = getelementptr %"string[4]_string[4]__tuple_t", ptr %"$kv", i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %2, i64 4, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"$kv", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!64} !llvm.module.flags = !{!66} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_map", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !22} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 32, elements: !20) !19 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !20 = !{!21} !21 = !DISubrange(count: 4, lowerBound: 0) !22 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !23 = !DIGlobalVariableExpression(var: !24, expr: !DIExpression()) !24 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !25, isLocal: false, isDefinition: true) !25 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !26) !26 = !{!5, !27, !28, !31} !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !29, size: 64, offset: 128) !29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64) !30 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !31 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !32, size: 64, offset: 192) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 64, elements: !34) !34 = !{!35, !36} !35 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 32) !36 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 32, offset: 32) !37 = !DIGlobalVariableExpression(var: !38, expr: !DIExpression()) !38 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !39, isLocal: false, isDefinition: true) !39 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !40) !40 = !{!41, !46} !41 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !42, size: 64) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !44) !44 = !{!45} !45 = !DISubrange(count: 27, lowerBound: 0) !46 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !47, size: 64, offset: 64) !47 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !48, size: 64) !48 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !49) !49 = !{!50} !50 = !DISubrange(count: 262144, lowerBound: 0) !51 = !DIGlobalVariableExpression(var: !52, expr: !DIExpression()) !52 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !53, isLocal: false, isDefinition: true) !53 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !54) !54 = !{!55, !27, !60, !63} !55 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !56, size: 64) !56 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !57, size: 64) !57 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !58) !58 = !{!59} !59 = !DISubrange(count: 2, lowerBound: 0) !60 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !61, size: 64, offset: 128) !61 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !62, size: 64) !62 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !63 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !29, size: 64, offset: 192) !64 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !65) !65 = !{!0, !23, !37, !51} !66 = !{i32 2, !"Debug Info Version", i32 3} !67 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !68, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !64, retainedNodes: !71) !68 = !DISubroutineType(types: !69) !69 = !{!30, !70} !70 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) !71 = !{!72} !72 = !DILocalVariable(name: "ctx", arg: 1, scope: !67, file: !2, type: !70) !73 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !74, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !64, retainedNodes: !76) !74 = !DISubroutineType(types: !75) !75 = !{!30, !70, !70, !70, !70} !76 = !{!77, !78, !79, !80} !77 = !DILocalVariable(name: "map", arg: 1, scope: !73, file: !2, type: !70) !78 = !DILocalVariable(name: "key", arg: 2, scope: !73, file: !2, type: !70) !79 = !DILocalVariable(name: "value", arg: 3, scope: !73, file: !2, type: !70) !80 = !DILocalVariable(name: "ctx", arg: 4, scope: !73, file: !2, type: !70) bpftrace-0.23.2/tests/codegen/llvm/for_map_two_keys.ll000066400000000000000000000233171477746507000230330ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %int64_int64__tuple_t = type { i64, i64 } %"(int64,int64)_int64__tuple_t" = type { %int64_int64__tuple_t, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_map = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_x = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !37 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !51 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !66 { entry: %"@map_val" = alloca i64, align 8 %tuple = alloca %int64_int64__tuple_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 16, i1 false) %1 = getelementptr %int64_int64__tuple_t, ptr %tuple, i32 0, i32 0 store i64 16, ptr %1, align 8 %2 = getelementptr %int64_int64__tuple_t, ptr %tuple, i32 0, i32 1 store i64 17, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_val") store i64 32, ptr %"@map_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_map, ptr %tuple, ptr %"@map_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_map, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !73 { %"@x_key" = alloca i64, align 8 %"$kv" = alloca %"(int64,int64)_int64__tuple_t", align 8 %val = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 24, i1 false) %5 = getelementptr %"(int64,int64)_int64__tuple_t", ptr %"$kv", i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %5, ptr align 1 %1, i64 16, i1 false) %6 = getelementptr %"(int64,int64)_int64__tuple_t", ptr %"$kv", i32 0, i32 1 store i64 %val, ptr %6, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"$kv", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!63} !llvm.module.flags = !{!65} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_map", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !23} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !22} !20 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !22 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 64, offset: 64) !23 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !24, size: 64, offset: 192) !24 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !28) !28 = !{!5, !29, !30, !31} !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !24, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !32, size: 64, offset: 192) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 192, elements: !34) !34 = !{!35, !36} !35 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 128) !36 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 64, offset: 128) !37 = !DIGlobalVariableExpression(var: !38, expr: !DIExpression()) !38 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !39, isLocal: false, isDefinition: true) !39 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !40) !40 = !{!41, !46} !41 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !42, size: 64) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !44) !44 = !{!45} !45 = !DISubrange(count: 27, lowerBound: 0) !46 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !47, size: 64, offset: 64) !47 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !48, size: 64) !48 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !49) !49 = !{!50} !50 = !DISubrange(count: 262144, lowerBound: 0) !51 = !DIGlobalVariableExpression(var: !52, expr: !DIExpression()) !52 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !53, isLocal: false, isDefinition: true) !53 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !54) !54 = !{!55, !29, !60, !23} !55 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !56, size: 64) !56 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !57, size: 64) !57 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !58) !58 = !{!59} !59 = !DISubrange(count: 2, lowerBound: 0) !60 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !61, size: 64, offset: 128) !61 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !62, size: 64) !62 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !63 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !64) !64 = !{!0, !25, !37, !51} !65 = !{i32 2, !"Debug Info Version", i32 3} !66 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !67, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !63, retainedNodes: !71) !67 = !DISubroutineType(types: !68) !68 = !{!21, !69} !69 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !70, size: 64) !70 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !71 = !{!72} !72 = !DILocalVariable(name: "ctx", arg: 1, scope: !66, file: !2, type: !69) !73 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !74, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !63, retainedNodes: !76) !74 = !DISubroutineType(types: !75) !75 = !{!21, !69, !69, !69, !69} !76 = !{!77, !78, !79, !80} !77 = !DILocalVariable(name: "map", arg: 1, scope: !73, file: !2, type: !69) !78 = !DILocalVariable(name: "key", arg: 2, scope: !73, file: !2, type: !69) !79 = !DILocalVariable(name: "value", arg: 3, scope: !73, file: !2, type: !69) !80 = !DILocalVariable(name: "ctx", arg: 4, scope: !73, file: !2, type: !69) bpftrace-0.23.2/tests/codegen/llvm/for_map_variables.ll000066400000000000000000000252351477746507000231400ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %ctx_t = type { ptr, ptr } %int64_int64__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_len = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_map = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !39 @abc = global [4 x i8] c"abc\00" @def = global [4 x i8] c"def\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !54 { entry: %"@len_val" = alloca i64, align 8 %"@len_key" = alloca i64, align 8 %ctx = alloca %ctx_t, align 8 %"$var3" = alloca [4 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var3") call void @llvm.memset.p0.i64(ptr align 1 %"$var3", i8 0, i64 4, i1 false) %"$var2" = alloca [4 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var2") call void @llvm.memset.p0.i64(ptr align 1 %"$var2", i8 0, i64 4, i1 false) %"$var1" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var1") store i64 0, ptr %"$var1", align 8 %"@map_val" = alloca i64, align 8 %"@map_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_key") store i64 16, ptr %"@map_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_val") store i64 32, ptr %"@map_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_map, ptr %"@map_key", ptr %"@map_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_key") store i64 123, ptr %"$var1", align 8 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$var2", ptr align 1 @abc, i64 4, i1 false) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$var3", ptr align 1 @def, i64 4, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %ctx) %1 = call ptr @llvm.preserve.static.offset(ptr %ctx) %"ctx.$var1" = getelementptr %ctx_t, ptr %1, i64 0, i32 0 store ptr %"$var1", ptr %"ctx.$var1", align 8 %2 = call ptr @llvm.preserve.static.offset(ptr %ctx) %"ctx.$var3" = getelementptr %ctx_t, ptr %2, i64 0, i32 1 store ptr %"$var3", ptr %"ctx.$var3", align 8 %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_map, ptr @map_for_each_cb, ptr %ctx, i64 0) %3 = load i64, ptr %"$var1", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@len_key") store i64 0, ptr %"@len_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@len_val") store i64 %3, ptr %"@len_val", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_len, ptr %"@len_key", ptr %"@len_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@len_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@len_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #4 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !61 { %"$can_read" = alloca [4 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$can_read") call void @llvm.memset.p0.i64(ptr align 1 %"$can_read", i8 0, i64 4, i1 false) %"$kv" = alloca %int64_int64__tuple_t, align 8 %key = load i64, ptr %1, align 8 %val = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %5 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %5, align 8 %6 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %val, ptr %6, align 8 %"ctx.$var1" = getelementptr %ctx_t, ptr %3, i64 0, i32 0 %"$var1" = load ptr, ptr %"ctx.$var1", align 8 %"ctx.$var3" = getelementptr %ctx_t, ptr %3, i64 0, i32 1 %"$var3" = load ptr, ptr %"ctx.$var3", align 8 %7 = load i64, ptr %"$var1", align 8 %8 = add i64 %7, 1 store i64 %8, ptr %"$var1", align 8 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$can_read", ptr align 1 %"$var3", i64 4, i1 false) ret i64 0 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!51} !llvm.module.flags = !{!53} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_len", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_map", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!5, !20, !12, !15} !20 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !21, size: 64, offset: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 4096, lowerBound: 0) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !11, !48, !15} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !49, size: 64, offset: 128) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !51 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !52) !52 = !{!0, !16, !25, !39} !53 = !{i32 2, !"Debug Info Version", i32 3} !54 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !55, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !51, retainedNodes: !59) !55 = !DISubroutineType(types: !56) !56 = !{!14, !57} !57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !58, size: 64) !58 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !54, file: !2, type: !57) !61 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !62, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !51, retainedNodes: !64) !62 = !DISubroutineType(types: !63) !63 = !{!14, !57, !57, !57, !57} !64 = !{!65, !66, !67, !68} !65 = !DILocalVariable(name: "map", arg: 1, scope: !61, file: !2, type: !57) !66 = !DILocalVariable(name: "key", arg: 2, scope: !61, file: !2, type: !57) !67 = !DILocalVariable(name: "value", arg: 3, scope: !61, file: !2, type: !57) !68 = !DILocalVariable(name: "ctx", arg: 4, scope: !61, file: !2, type: !57) bpftrace-0.23.2/tests/codegen/llvm/for_map_variables_multiple_loops.ll000066400000000000000000000246601477746507000262700ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %ctx_t.2 = type { ptr, ptr } %ctx_t = type { ptr } %int64_int64__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !50 { entry: %ctx1 = alloca %ctx_t.2, align 8 %ctx = alloca %ctx_t, align 8 %"$var2" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var2") store i64 0, ptr %"$var2", align 8 %"$var1" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var1") store i64 0, ptr %"$var1", align 8 %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 0, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"$var1", align 8 store i64 0, ptr %"$var2", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %ctx) %1 = call ptr @llvm.preserve.static.offset(ptr %ctx) %"ctx.$var1" = getelementptr %ctx_t, ptr %1, i64 0, i32 0 store ptr %"$var1", ptr %"ctx.$var1", align 8 %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_, ptr @map_for_each_cb, ptr %ctx, i64 0) call void @llvm.lifetime.start.p0(i64 -1, ptr %ctx1) %2 = call ptr @llvm.preserve.static.offset(ptr %ctx1) %"ctx.$var12" = getelementptr %ctx_t.2, ptr %2, i64 0, i32 0 store ptr %"$var1", ptr %"ctx.$var12", align 8 %3 = call ptr @llvm.preserve.static.offset(ptr %ctx1) %"ctx.$var2" = getelementptr %ctx_t.2, ptr %3, i64 0, i32 1 store ptr %"$var2", ptr %"ctx.$var2", align 8 %for_each_map_elem3 = call i64 inttoptr (i64 164 to ptr)(ptr @AT_, ptr @map_for_each_cb.1, ptr %ctx1, i64 0) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #2 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !57 { %"$_" = alloca %int64_int64__tuple_t, align 8 %key = load i64, ptr %1, align 8 %val = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$_") call void @llvm.memset.p0.i64(ptr align 1 %"$_", i8 0, i64 16, i1 false) %5 = getelementptr %int64_int64__tuple_t, ptr %"$_", i32 0, i32 0 store i64 %key, ptr %5, align 8 %6 = getelementptr %int64_int64__tuple_t, ptr %"$_", i32 0, i32 1 store i64 %val, ptr %6, align 8 %"ctx.$var1" = getelementptr %ctx_t, ptr %3, i64 0, i32 0 %"$var1" = load ptr, ptr %"ctx.$var1", align 8 %7 = load i64, ptr %"$var1", align 8 %8 = add i64 %7, 1 store i64 %8, ptr %"$var1", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 define internal i64 @map_for_each_cb.1(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !65 { %"$_" = alloca %int64_int64__tuple_t, align 8 %key = load i64, ptr %1, align 8 %val = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$_") call void @llvm.memset.p0.i64(ptr align 1 %"$_", i8 0, i64 16, i1 false) %5 = getelementptr %int64_int64__tuple_t, ptr %"$_", i32 0, i32 0 store i64 %key, ptr %5, align 8 %6 = getelementptr %int64_int64__tuple_t, ptr %"$_", i32 0, i32 1 store i64 %val, ptr %6, align 8 %"ctx.$var1" = getelementptr %ctx_t.2, ptr %3, i64 0, i32 0 %"$var1" = load ptr, ptr %"ctx.$var1", align 8 %"ctx.$var2" = getelementptr %ctx_t.2, ptr %3, i64 0, i32 1 %"$var2" = load ptr, ptr %"ctx.$var2", align 8 %7 = load i64, ptr %"$var1", align 8 %8 = add i64 %7, 1 store i64 %8, ptr %"$var1", align 8 %9 = load i64, ptr %"$var2", align 8 %10 = add i64 %9, 1 store i64 %10, ptr %"$var2", align 8 ret i64 0 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) !57 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !47, retainedNodes: !60) !58 = !DISubroutineType(types: !59) !59 = !{!18, !53, !53, !53, !53} !60 = !{!61, !62, !63, !64} !61 = !DILocalVariable(name: "map", arg: 1, scope: !57, file: !2, type: !53) !62 = !DILocalVariable(name: "key", arg: 2, scope: !57, file: !2, type: !53) !63 = !DILocalVariable(name: "value", arg: 3, scope: !57, file: !2, type: !53) !64 = !DILocalVariable(name: "ctx", arg: 4, scope: !57, file: !2, type: !53) !65 = distinct !DISubprogram(name: "map_for_each_cb_1", linkageName: "map_for_each_cb_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !47, retainedNodes: !66) !66 = !{!67, !68, !69, !70} !67 = !DILocalVariable(name: "map", arg: 1, scope: !65, file: !2, type: !53) !68 = !DILocalVariable(name: "key", arg: 2, scope: !65, file: !2, type: !53) !69 = !DILocalVariable(name: "value", arg: 3, scope: !65, file: !2, type: !53) !70 = !DILocalVariable(name: "ctx", arg: 4, scope: !65, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/for_map_variables_scope.ll000066400000000000000000000216471477746507000243340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %int64_int64__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_map = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !50 { entry: %"@map_val" = alloca i64, align 8 %"@map_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_key") store i64 16, ptr %"@map_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_val") store i64 32, ptr %"@map_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_map, ptr %"@map_key", ptr %"@map_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_key") %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_map, ptr @map_for_each_cb, ptr null, i64 0) %for_each_map_elem1 = call i64 inttoptr (i64 164 to ptr)(ptr @AT_map, ptr @map_for_each_cb.1, ptr null, i64 0) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !57 { %"$var" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var") store i64 0, ptr %"$var", align 8 %"$kv" = alloca %int64_int64__tuple_t, align 8 %key = load i64, ptr %1, align 8 %val = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %5 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %5, align 8 %6 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %val, ptr %6, align 8 store i64 1, ptr %"$var", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 define internal i64 @map_for_each_cb.1(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !65 { %"$var" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var") store i64 0, ptr %"$var", align 8 %"$kv" = alloca %int64_int64__tuple_t, align 8 %key = load i64, ptr %1, align 8 %val = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %5 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %5, align 8 %6 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %val, ptr %6, align 8 store i64 1, ptr %"$var", align 8 ret i64 0 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_map", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) !57 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !47, retainedNodes: !60) !58 = !DISubroutineType(types: !59) !59 = !{!18, !53, !53, !53, !53} !60 = !{!61, !62, !63, !64} !61 = !DILocalVariable(name: "map", arg: 1, scope: !57, file: !2, type: !53) !62 = !DILocalVariable(name: "key", arg: 2, scope: !57, file: !2, type: !53) !63 = !DILocalVariable(name: "value", arg: 3, scope: !57, file: !2, type: !53) !64 = !DILocalVariable(name: "ctx", arg: 4, scope: !57, file: !2, type: !53) !65 = distinct !DISubprogram(name: "map_for_each_cb_1", linkageName: "map_for_each_cb_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !47, retainedNodes: !66) !66 = !{!67, !68, !69, !70} !67 = !DILocalVariable(name: "map", arg: 1, scope: !65, file: !2, type: !53) !68 = !DILocalVariable(name: "key", arg: 2, scope: !65, file: !2, type: !53) !69 = !DILocalVariable(name: "value", arg: 3, scope: !65, file: !2, type: !53) !70 = !DILocalVariable(name: "ctx", arg: 4, scope: !65, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/if_else_printf.ll000066400000000000000000000201401477746507000224430ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %printf_t.1 = type { i64 } %printf_t = type { i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key6 = alloca i32, align 4 %printf_args1 = alloca %printf_t.1, align 8 %key = alloca i32, align 4 %printf_args = alloca %printf_t, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %3 = icmp ugt i64 %2, 10 %true_cond = icmp ne i1 %3, false br i1 %true_cond, label %if_body, label %else_body if_body: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %printf_args) call void @llvm.memset.p0.i64(ptr align 1 %printf_args, i8 0, i64 8, i1 false) %4 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 0 store i64 0, ptr %4, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %printf_args, i64 8, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge if_end: ; preds = %counter_merge4, %counter_merge ret i64 0 else_body: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %printf_args1) call void @llvm.memset.p0.i64(ptr align 1 %printf_args1, i8 0, i64 8, i1 false) %5 = getelementptr %printf_t.1, ptr %printf_args1, i32 0, i32 0 store i64 1, ptr %5, align 8 %ringbuf_output2 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %printf_args1, i64 8, i64 0) %ringbuf_loss5 = icmp slt i64 %ringbuf_output2, 0 br i1 %ringbuf_loss5, label %event_loss_counter3, label %counter_merge4 event_loss_counter: ; preds = %if_body call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %if_body call void @llvm.lifetime.end.p0(i64 -1, ptr %printf_args) br label %if_end lookup_success: ; preds = %event_loss_counter %6 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge event_loss_counter3: ; preds = %else_body call void @llvm.lifetime.start.p0(i64 -1, ptr %key6) store i32 0, ptr %key6, align 4 %lookup_elem7 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key6) %map_lookup_cond11 = icmp ne ptr %lookup_elem7, null br i1 %map_lookup_cond11, label %lookup_success8, label %lookup_failure9 counter_merge4: ; preds = %lookup_merge10, %else_body call void @llvm.lifetime.end.p0(i64 -1, ptr %printf_args1) br label %if_end lookup_success8: ; preds = %event_loss_counter3 %7 = atomicrmw add ptr %lookup_elem7, i64 1 seq_cst, align 8 br label %lookup_merge10 lookup_failure9: ; preds = %event_loss_counter3 br label %lookup_merge10 lookup_merge10: ; preds = %lookup_failure9, %lookup_success8 call void @llvm.lifetime.end.p0(i64 -1, ptr %key6) br label %counter_merge4 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/if_else_variable.ll000066400000000000000000000114331477746507000227330ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"$s1" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$s1") store i64 0, ptr %"$s1", align 8 %"$s" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$s") store i64 0, ptr %"$s", align 8 br i1 true, label %if_body, label %else_body if_body: ; preds = %entry store i64 10, ptr %"$s", align 8 br label %if_end if_end: ; preds = %else_body, %if_body ret i64 0 else_body: ; preds = %entry store i64 20, ptr %"$s1", align 8 br label %if_end } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/if_nested_printf.ll000066400000000000000000000156631477746507000230130ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %printf_t = type { i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %printf_args = alloca %printf_t, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %3 = icmp ugt i64 %2, 10000 %true_cond = icmp ne i1 %3, false br i1 %true_cond, label %if_body, label %if_end if_body: ; preds = %entry %get_pid_tgid3 = call i64 inttoptr (i64 14 to ptr)() %4 = lshr i64 %get_pid_tgid3, 32 %pid4 = trunc i64 %4 to i32 %5 = zext i32 %pid4 to i64 %6 = urem i64 %5, 2 %7 = icmp eq i64 %6, 0 %true_cond5 = icmp ne i1 %7, false br i1 %true_cond5, label %if_body1, label %if_end2 if_end: ; preds = %if_end2, %entry ret i64 0 if_body1: ; preds = %if_body call void @llvm.lifetime.start.p0(i64 -1, ptr %printf_args) call void @llvm.memset.p0.i64(ptr align 1 %printf_args, i8 0, i64 8, i1 false) %8 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 0 store i64 0, ptr %8, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %printf_args, i64 8, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge if_end2: ; preds = %counter_merge, %if_body br label %if_end event_loss_counter: ; preds = %if_body1 call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %if_body1 call void @llvm.lifetime.end.p0(i64 -1, ptr %printf_args) br label %if_end2 lookup_success: ; preds = %event_loss_counter %9 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/if_printf.ll000066400000000000000000000153441477746507000214450ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %printf_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %printf_args = alloca %printf_t, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %3 = icmp ugt i64 %2, 10000 %true_cond = icmp ne i1 %3, false br i1 %true_cond, label %if_body, label %if_end if_body: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %printf_args) call void @llvm.memset.p0.i64(ptr align 1 %printf_args, i8 0, i64 16, i1 false) %4 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 0 store i64 0, ptr %4, align 8 %get_pid_tgid1 = call i64 inttoptr (i64 14 to ptr)() %5 = lshr i64 %get_pid_tgid1, 32 %pid2 = trunc i64 %5 to i32 %6 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 1 %7 = zext i32 %pid2 to i64 store i64 %7, ptr %6, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %printf_args, i64 16, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge if_end: ; preds = %counter_merge, %entry ret i64 0 event_loss_counter: ; preds = %if_body call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %if_body call void @llvm.lifetime.end.p0(i64 -1, ptr %printf_args) br label %if_end lookup_success: ; preds = %event_loss_counter %8 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/if_variable.ll000066400000000000000000000113351477746507000217240ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"$y" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$y") store i64 0, ptr %"$y", align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 br i1 true, label %if_body, label %if_end if_body: ; preds = %entry store i64 10, ptr %"$x", align 8 br label %if_end if_end: ; preds = %if_body, %entry %1 = load i64, ptr %"$x", align 8 store i64 %1, ptr %"$y", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/int_propagation.ll000066400000000000000000000164201477746507000226560ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1234, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") store i64 %2, ptr %"@y_val", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/intcast_call.ll000066400000000000000000000162771477746507000221330ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !46 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kretprobe_f_1(ptr %0) section "s_kretprobe_f_1" !dbg !51 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 10 %retval = load volatile i64, ptr %2, align 8 %cast = trunc i64 %retval to i32 %3 = sext i32 %cast to i64 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %4 = load i64, ptr %lookup_elem, align 8 %5 = add i64 %4, %3 store i64 %5, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 %3, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !11, !43, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !44, size: 64, offset: 128) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !46 = !DIGlobalVariableExpression(var: !47, expr: !DIExpression()) !47 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !20, !34, !46} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kretprobe_f_1", linkageName: "kretprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !56) !52 = !DISubroutineType(types: !53) !53 = !{!18, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !56 = !{!57} !57 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/intcast_retval.ll000066400000000000000000000136311477746507000225040ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kretprobe_f_1(ptr %0) section "s_kretprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 10 %retval = load volatile i64, ptr %2, align 8 %cast = trunc i64 %retval to i32 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") %3 = sext i32 %cast to i64 store i64 %3, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kretprobe_f_1", linkageName: "kretprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/intptrcast_assign_var.ll000066400000000000000000000142201477746507000240640ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kretprobe_f_1(ptr %0) section "s_kretprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %deref = alloca i8, align 1 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 4 %reg_bp = load volatile i64, ptr %2, align 8 %3 = sub i64 %reg_bp, 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %deref) %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %deref, i32 1, i64 %3) %4 = load i8, ptr %deref, align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %deref) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") %5 = sext i8 %4 to i64 store i64 %5, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kretprobe_f_1", linkageName: "kretprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/intptrcast_call.ll000066400000000000000000000166661477746507000226630ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !46 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kretprobe_f_1(ptr %0) section "s_kretprobe_f_1" !dbg !51 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %deref = alloca i8, align 1 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 4 %reg_bp = load volatile i64, ptr %2, align 8 %3 = sub i64 %reg_bp, 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %deref) %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %deref, i32 1, i64 %3) %4 = load i8, ptr %deref, align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %deref) %5 = sext i8 %4 to i64 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %6 = load i64, ptr %lookup_elem, align 8 %7 = add i64 %6, %5 store i64 %7, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 %5, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !11, !43, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !44, size: 64, offset: 128) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !46 = !DIGlobalVariableExpression(var: !47, expr: !DIExpression()) !47 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !20, !34, !46} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kretprobe_f_1", linkageName: "kretprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !56) !52 = !DISubroutineType(types: !53) !53 = !{!18, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !56 = !{!57} !57 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/iter_dereference.ll000066400000000000000000000151021477746507000227470ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @iter_task_file_1(ptr %0) section "s_iter_task_file_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i8, ptr %1, i64 0 %3 = load volatile i64, ptr %2, align 8 %predcond = icmp eq i64 %3, 0 br i1 %predcond, label %pred_false, label %pred_true pred_false: ; preds = %entry ret i64 0 pred_true: ; preds = %entry %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 8 %7 = load volatile i64, ptr %6, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %7, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "iter_task_file_1", linkageName: "iter_task_file_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/late_variable_decl.ll000066400000000000000000000220611477746507000232400ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %int64_int64__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_map = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !50 { entry: %"$x3" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x3") store i64 0, ptr %"$x3", align 8 %"@map_val" = alloca i64, align 8 %"@map_key" = alloca i64, align 8 %"$x2" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x2") store i64 0, ptr %"$x2", align 8 %"$i" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$i") store i64 0, ptr %"$i", align 8 %"$x1" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x1") store i64 0, ptr %"$x1", align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 br i1 true, label %if_body, label %if_end if_body: ; preds = %entry store i64 1, ptr %"$x", align 8 br label %if_end if_end: ; preds = %if_body, %entry store i64 2, ptr %"$x1", align 8 store i64 1, ptr %"$i", align 8 br label %while_cond while_cond: ; preds = %while_body, %if_end %1 = load i64, ptr %"$i", align 8 %true_cond = icmp ne i64 %1, 0 br i1 %true_cond, label %while_body, label %while_end, !llvm.loop !57 while_body: ; preds = %while_cond %2 = load i64, ptr %"$i", align 8 %3 = sub i64 %2, 1 store i64 %3, ptr %"$i", align 8 store i64 3, ptr %"$x2", align 8 br label %while_cond while_end: ; preds = %while_cond call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_key") store i64 16, ptr %"@map_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_val") store i64 32, ptr %"@map_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_map, ptr %"@map_key", ptr %"@map_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_key") %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_map, ptr @map_for_each_cb, ptr null, i64 0) store i64 5, ptr %"$x3", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !59 { %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 %"$kv" = alloca %int64_int64__tuple_t, align 8 %key = load i64, ptr %1, align 8 %val = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %5 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %5, align 8 %6 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %val, ptr %6, align 8 store i64 4, ptr %"$x", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_map", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) !57 = distinct !{!57, !58} !58 = !{!"llvm.loop.unroll.disable"} !59 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !60, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !47, retainedNodes: !62) !60 = !DISubroutineType(types: !61) !61 = !{!18, !53, !53, !53, !53} !62 = !{!63, !64, !65, !66} !63 = !DILocalVariable(name: "map", arg: 1, scope: !59, file: !2, type: !53) !64 = !DILocalVariable(name: "key", arg: 2, scope: !59, file: !2, type: !53) !65 = !DILocalVariable(name: "value", arg: 3, scope: !59, file: !2, type: !53) !66 = !DILocalVariable(name: "ctx", arg: 4, scope: !59, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/logical_and.ll000066400000000000000000000151141477746507000217140ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"&&_result" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"&&_result") %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %3 = icmp ne i64 %2, 1234 %lhs_true_cond = icmp ne i1 %3, false br i1 %lhs_true_cond, label %"&&_lhs_true", label %"&&_false" "&&_lhs_true": ; preds = %entry %get_pid_tgid1 = call i64 inttoptr (i64 14 to ptr)() %4 = lshr i64 %get_pid_tgid1, 32 %pid2 = trunc i64 %4 to i32 %5 = zext i32 %pid2 to i64 %6 = icmp ne i64 %5, 1235 %rhs_true_cond = icmp ne i1 %6, false br i1 %rhs_true_cond, label %"&&_true", label %"&&_false" "&&_true": ; preds = %"&&_lhs_true" store i64 1, ptr %"&&_result", align 8 br label %"&&_merge" "&&_false": ; preds = %"&&_lhs_true", %entry store i64 0, ptr %"&&_result", align 8 br label %"&&_merge" "&&_merge": ; preds = %"&&_false", %"&&_true" %7 = load i64, ptr %"&&_result", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %7, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/logical_and_or_different_type.ll000066400000000000000000000312051477746507000255020ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %printf_t = type { i64, i64, i64, i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @fmt_str_buf = dso_local externally_initialized global [1 x [1 x [40 x i8]]] zeroinitializer, section ".data.fmt_str_buf", !dbg !38 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !49 { entry: %key = alloca i32, align 4 %"struct Foo.m16" = alloca i32, align 4 %"||_result15" = alloca i64, align 8 %"struct Foo.m8" = alloca i32, align 4 %"||_result" = alloca i64, align 8 %"struct Foo.m6" = alloca i32, align 4 %"&&_result5" = alloca i64, align 8 %"struct Foo.m" = alloca i32, align 4 %"&&_result" = alloca i64, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 store i64 0, ptr %"$foo", align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [40 x i8]]], ptr @fmt_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 call void @llvm.memset.p0.i64(ptr align 1 %2, i8 0, i64 40, i1 false) %3 = getelementptr %printf_t, ptr %2, i32 0, i32 0 store i64 0, ptr %3, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"&&_result") %4 = load i64, ptr %"$foo", align 8 %5 = inttoptr i64 %4 to ptr %6 = call ptr @llvm.preserve.static.offset(ptr %5) %7 = getelementptr i8, ptr %6, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.m") %probe_read = call i64 inttoptr (i64 4 to ptr)(ptr %"struct Foo.m", i32 4, ptr %7) %8 = load i32, ptr %"struct Foo.m", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.m") %lhs_true_cond = icmp ne i32 %8, 0 br i1 %lhs_true_cond, label %"&&_lhs_true", label %"&&_false" "&&_lhs_true": ; preds = %entry br i1 false, label %"&&_true", label %"&&_false" "&&_true": ; preds = %"&&_lhs_true" store i64 1, ptr %"&&_result", align 8 br label %"&&_merge" "&&_false": ; preds = %"&&_lhs_true", %entry store i64 0, ptr %"&&_result", align 8 br label %"&&_merge" "&&_merge": ; preds = %"&&_false", %"&&_true" %9 = load i64, ptr %"&&_result", align 8 %10 = getelementptr %printf_t, ptr %2, i32 0, i32 1 store i64 %9, ptr %10, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"&&_result5") br i1 true, label %"&&_lhs_true1", label %"&&_false3" "&&_lhs_true1": ; preds = %"&&_merge" %11 = load i64, ptr %"$foo", align 8 %12 = inttoptr i64 %11 to ptr %13 = call ptr @llvm.preserve.static.offset(ptr %12) %14 = getelementptr i8, ptr %13, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.m6") %probe_read7 = call i64 inttoptr (i64 4 to ptr)(ptr %"struct Foo.m6", i32 4, ptr %14) %15 = load i32, ptr %"struct Foo.m6", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.m6") %rhs_true_cond = icmp ne i32 %15, 0 br i1 %rhs_true_cond, label %"&&_true2", label %"&&_false3" "&&_true2": ; preds = %"&&_lhs_true1" store i64 1, ptr %"&&_result5", align 8 br label %"&&_merge4" "&&_false3": ; preds = %"&&_lhs_true1", %"&&_merge" store i64 0, ptr %"&&_result5", align 8 br label %"&&_merge4" "&&_merge4": ; preds = %"&&_false3", %"&&_true2" %16 = load i64, ptr %"&&_result5", align 8 %17 = getelementptr %printf_t, ptr %2, i32 0, i32 2 store i64 %16, ptr %17, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"||_result") %18 = load i64, ptr %"$foo", align 8 %19 = inttoptr i64 %18 to ptr %20 = call ptr @llvm.preserve.static.offset(ptr %19) %21 = getelementptr i8, ptr %20, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.m8") %probe_read9 = call i64 inttoptr (i64 4 to ptr)(ptr %"struct Foo.m8", i32 4, ptr %21) %22 = load i32, ptr %"struct Foo.m8", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.m8") %lhs_true_cond10 = icmp ne i32 %22, 0 br i1 %lhs_true_cond10, label %"||_true", label %"||_lhs_false" "||_lhs_false": ; preds = %"&&_merge4" br i1 false, label %"||_true", label %"||_false" "||_false": ; preds = %"||_lhs_false" store i64 0, ptr %"||_result", align 8 br label %"||_merge" "||_true": ; preds = %"||_lhs_false", %"&&_merge4" store i64 1, ptr %"||_result", align 8 br label %"||_merge" "||_merge": ; preds = %"||_true", %"||_false" %23 = load i64, ptr %"||_result", align 8 %24 = getelementptr %printf_t, ptr %2, i32 0, i32 3 store i64 %23, ptr %24, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"||_result15") br i1 false, label %"||_true13", label %"||_lhs_false11" "||_lhs_false11": ; preds = %"||_merge" %25 = load i64, ptr %"$foo", align 8 %26 = inttoptr i64 %25 to ptr %27 = call ptr @llvm.preserve.static.offset(ptr %26) %28 = getelementptr i8, ptr %27, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.m16") %probe_read17 = call i64 inttoptr (i64 4 to ptr)(ptr %"struct Foo.m16", i32 4, ptr %28) %29 = load i32, ptr %"struct Foo.m16", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.m16") %rhs_true_cond18 = icmp ne i32 %29, 0 br i1 %rhs_true_cond18, label %"||_true13", label %"||_false12" "||_false12": ; preds = %"||_lhs_false11" store i64 0, ptr %"||_result15", align 8 br label %"||_merge14" "||_true13": ; preds = %"||_lhs_false11", %"||_merge" store i64 1, ptr %"||_result15", align 8 br label %"||_merge14" "||_merge14": ; preds = %"||_true13", %"||_false12" %30 = load i64, ptr %"||_result15", align 8 %31 = getelementptr %printf_t, ptr %2, i32 0, i32 4 store i64 %30, ptr %31, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %2, i64 40, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge event_loss_counter: ; preds = %"||_merge14" call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %"||_merge14" ret i64 0 lookup_success: ; preds = %event_loss_counter %32 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "fmt_str_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 320, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 320, elements: !28) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 320, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 40, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/logical_not.ll000066400000000000000000000144001477746507000217470ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !47 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 0, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") store i64 1, ptr %"@y_val", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/logical_or.ll000066400000000000000000000151161477746507000215740ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"||_result" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"||_result") %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %3 = icmp eq i64 %2, 1234 %lhs_true_cond = icmp ne i1 %3, false br i1 %lhs_true_cond, label %"||_true", label %"||_lhs_false" "||_lhs_false": ; preds = %entry %get_pid_tgid1 = call i64 inttoptr (i64 14 to ptr)() %4 = lshr i64 %get_pid_tgid1, 32 %pid2 = trunc i64 %4 to i32 %5 = zext i32 %pid2 to i64 %6 = icmp eq i64 %5, 1235 %rhs_true_cond = icmp ne i1 %6, false br i1 %rhs_true_cond, label %"||_true", label %"||_false" "||_false": ; preds = %"||_lhs_false" store i64 0, ptr %"||_result", align 8 br label %"||_merge" "||_true": ; preds = %"||_lhs_false", %entry store i64 1, ptr %"||_result", align 8 br label %"||_merge" "||_merge": ; preds = %"||_true", %"||_false" %7 = load i64, ptr %"||_result", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %7, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/macro_definition.ll000066400000000000000000000127171477746507000227770ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 100, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/map_args.ll000066400000000000000000000164261477746507000212600ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"uprobe:/tmp/bpftrace-test-dwarf-data:func_1_args" = type { i32, i64, i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @uprobe__tmp_bpftrace_test_dwarf_data_func_1_1(ptr %0) section "s_uprobe__tmp_bpftrace_test_dwarf_data_func_1_1" !dbg !51 { entry: %"@_key" = alloca i64, align 8 %args = alloca %"uprobe:/tmp/bpftrace-test-dwarf-data:func_1_args", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %args) %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = trunc i64 %arg0 to i32 %4 = getelementptr %"uprobe:/tmp/bpftrace-test-dwarf-data:func_1_args", ptr %args, i64 0, i32 0 store i32 %3, ptr %4, align 4 %5 = call ptr @llvm.preserve.static.offset(ptr %0) %6 = getelementptr i64, ptr %5, i64 13 %arg1 = load volatile i64, ptr %6, align 8 %7 = getelementptr %"uprobe:/tmp/bpftrace-test-dwarf-data:func_1_args", ptr %args, i64 0, i32 1 store i64 %arg1, ptr %7, align 8 %8 = call ptr @llvm.preserve.static.offset(ptr %0) %9 = getelementptr i64, ptr %8, i64 12 %arg2 = load volatile i64, ptr %9, align 8 %10 = getelementptr %"uprobe:/tmp/bpftrace-test-dwarf-data:func_1_args", ptr %args, i64 0, i32 2 store i64 %arg2, ptr %10, align 8 %11 = call ptr @llvm.preserve.static.offset(ptr %0) %12 = getelementptr i64, ptr %11, i64 11 %arg3 = load volatile i64, ptr %12, align 8 %13 = getelementptr %"uprobe:/tmp/bpftrace-test-dwarf-data:func_1_args", ptr %args, i64 0, i32 3 store i64 %arg3, ptr %13, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %args, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 224, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 28, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "uprobe__tmp_bpftrace_test_dwarf_data_func_1_1", linkageName: "uprobe__tmp_bpftrace_test_dwarf_data_func_1_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/map_assign_array.ll000066400000000000000000000207671477746507000230110ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !55 { entry: %"$var" = alloca i32, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var") store i32 0, ptr %"$var", align 4 %lookup_elem_val = alloca [4 x i32], align 4 %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca [4 x i32], align 4 %"@x_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = inttoptr i64 %arg0 to ptr %4 = call ptr @llvm.preserve.static.offset(ptr %3) %5 = getelementptr i8, ptr %4, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"@x_val", i32 16, ptr %5) %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.memcpy.p0.p0.i64(ptr align 1 %lookup_elem_val, ptr align 1 %lookup_elem, i64 16, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.memset.p0.i64(ptr align 1 %lookup_elem_val, i8 0, i64 16, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") %6 = getelementptr [4 x i32], ptr %lookup_elem_val, i32 0, i64 0 %7 = load volatile i32, ptr %6, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) store i32 %7, ptr %"$var", align 4 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #4 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!52} !llvm.module.flags = !{!54} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 128, elements: !23) !22 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !23 = !{!24} !24 = !DISubrange(count: 4, lowerBound: 0) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !48, !49, !51} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !51 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !52 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !53) !53 = !{!0, !25, !39} !54 = !{i32 2, !"Debug Info Version", i32 3} !55 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !52, retainedNodes: !60) !56 = !DISubroutineType(types: !57) !57 = !{!18, !58} !58 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !59, size: 64) !59 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !60 = !{!61} !61 = !DILocalVariable(name: "ctx", arg: 1, scope: !55, file: !2, type: !58) bpftrace-0.23.2/tests/codegen/llvm/map_assign_int.ll000066400000000000000000000127321477746507000224560ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/map_assign_string.ll000066400000000000000000000132071477746507000231700ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @blah = global [5 x i8] c"blah\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr @blah, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 40, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 5, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/map_assign_string_shorter.ll000066400000000000000000000154661477746507000247470ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @xxxxx = global [6 x i8] c"xxxxx\00" @a = global [2 x i8] c"a\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@x_val" = alloca [6 x i8], align 1 %"@x_key1" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr @xxxxx, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") call void @llvm.memset.p0.i64(ptr align 1 %"@x_val", i8 0, i64 6, i1 false) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"@x_val", ptr align 1 @a, i64 2, i1 false) %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key1", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 48, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 6, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/map_increment_decrement.ll000066400000000000000000000257701477746507000243400ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !45 { entry: %"@x_newval28" = alloca i64, align 8 %lookup_elem_val26 = alloca i64, align 8 %"@x_key21" = alloca i64, align 8 %"@x_newval19" = alloca i64, align 8 %lookup_elem_val17 = alloca i64, align 8 %"@x_key12" = alloca i64, align 8 %"@x_newval10" = alloca i64, align 8 %lookup_elem_val8 = alloca i64, align 8 %"@x_key3" = alloca i64, align 8 %"@x_newval" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 10, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_newval") %3 = add i64 %2, 1 store i64 %3, ptr %"@x_newval", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key1", ptr %"@x_newval", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_newval") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key3") store i64 0, ptr %"@x_key3", align 8 %lookup_elem4 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key3") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val8) %map_lookup_cond9 = icmp ne ptr %lookup_elem4, null br i1 %map_lookup_cond9, label %lookup_success5, label %lookup_failure6 lookup_success5: ; preds = %lookup_merge %4 = load i64, ptr %lookup_elem4, align 8 store i64 %4, ptr %lookup_elem_val8, align 8 br label %lookup_merge7 lookup_failure6: ; preds = %lookup_merge store i64 0, ptr %lookup_elem_val8, align 8 br label %lookup_merge7 lookup_merge7: ; preds = %lookup_failure6, %lookup_success5 %5 = load i64, ptr %lookup_elem_val8, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val8) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_newval10") %6 = add i64 %5, 1 store i64 %6, ptr %"@x_newval10", align 8 %update_elem11 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key3", ptr %"@x_newval10", i64 0) %7 = load i64, ptr %"@x_newval10", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_newval10") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key3") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key12") store i64 0, ptr %"@x_key12", align 8 %lookup_elem13 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key12") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val17) %map_lookup_cond18 = icmp ne ptr %lookup_elem13, null br i1 %map_lookup_cond18, label %lookup_success14, label %lookup_failure15 lookup_success14: ; preds = %lookup_merge7 %8 = load i64, ptr %lookup_elem13, align 8 store i64 %8, ptr %lookup_elem_val17, align 8 br label %lookup_merge16 lookup_failure15: ; preds = %lookup_merge7 store i64 0, ptr %lookup_elem_val17, align 8 br label %lookup_merge16 lookup_merge16: ; preds = %lookup_failure15, %lookup_success14 %9 = load i64, ptr %lookup_elem_val17, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val17) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_newval19") %10 = sub i64 %9, 1 store i64 %10, ptr %"@x_newval19", align 8 %update_elem20 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key12", ptr %"@x_newval19", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_newval19") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key12") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key21") store i64 0, ptr %"@x_key21", align 8 %lookup_elem22 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key21") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val26) %map_lookup_cond27 = icmp ne ptr %lookup_elem22, null br i1 %map_lookup_cond27, label %lookup_success23, label %lookup_failure24 lookup_success23: ; preds = %lookup_merge16 %11 = load i64, ptr %lookup_elem22, align 8 store i64 %11, ptr %lookup_elem_val26, align 8 br label %lookup_merge25 lookup_failure24: ; preds = %lookup_merge16 store i64 0, ptr %lookup_elem_val26, align 8 br label %lookup_merge25 lookup_merge25: ; preds = %lookup_failure24, %lookup_success23 %12 = load i64, ptr %lookup_elem_val26, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val26) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_newval28") %13 = sub i64 %12, 1 store i64 %13, ptr %"@x_newval28", align 8 %update_elem29 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key21", ptr %"@x_newval28", i64 0) %14 = load i64, ptr %"@x_newval28", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_newval28") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key21") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/map_key_array.ll000066400000000000000000000150731477746507000223070ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !54 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca [4 x i32], align 4 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = inttoptr i64 %arg0 to ptr %4 = call ptr @llvm.preserve.static.offset(ptr %3) %5 = getelementptr i8, ptr %4, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"@x_key", i32 16, ptr %5) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 44, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!51} !llvm.module.flags = !{!53} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !22} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 128, elements: !20) !19 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !20 = !{!21} !21 = !DISubrange(count: 4, lowerBound: 0) !22 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !23, size: 64, offset: 192) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !48, !49, !22} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) !51 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !52) !52 = !{!0, !25, !39} !53 = !{i32 2, !"Debug Info Version", i32 3} !54 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !55, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !51, retainedNodes: !59) !55 = !DISubroutineType(types: !56) !56 = !{!24, !57} !57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !58, size: 64) !58 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !54, file: !2, type: !57) bpftrace-0.23.2/tests/codegen/llvm/map_key_int.ll000066400000000000000000000156631477746507000217700ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %int64_int64_int64__tuple_t = type { i64, i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !56 { entry: %"@x_val" = alloca i64, align 8 %tuple = alloca %int64_int64_int64__tuple_t, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 24, i1 false) %1 = getelementptr %int64_int64_int64__tuple_t, ptr %tuple, i32 0, i32 0 store i64 11, ptr %1, align 8 %2 = getelementptr %int64_int64_int64__tuple_t, ptr %tuple, i32 0, i32 1 store i64 22, ptr %2, align 8 %3 = getelementptr %int64_int64_int64__tuple_t, ptr %tuple, i32 0, i32 2 store i64 33, ptr %3, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 44, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %tuple, ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!53} !llvm.module.flags = !{!55} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !24} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 192, elements: !19) !19 = !{!20, !22, !23} !20 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !22 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 64, offset: 64) !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 64, offset: 128) !24 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !25, size: 64, offset: 192) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !49, !50, !24} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !50 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !51, size: 64, offset: 128) !51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !52, size: 64) !52 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !53 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !54) !54 = !{!0, !26, !40} !55 = !{i32 2, !"Debug Info Version", i32 3} !56 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !57, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !53, retainedNodes: !61) !57 = !DISubroutineType(types: !58) !58 = !{!21, !59} !59 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !60, size: 64) !60 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !56, file: !2, type: !59) bpftrace-0.23.2/tests/codegen/llvm/map_key_probe.ll000066400000000000000000000207211477746507000222740ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !37 @"tracepoint:sched:sched_one" = global [27 x i8] c"tracepoint:sched:sched_one\00" @"tracepoint:sched:sched_two" = global [27 x i8] c"tracepoint:sched:sched_two\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_sched_sched_one_1(ptr %0) section "s_tracepoint_sched_sched_one_1" !dbg !53 { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr @"tracepoint:sched:sched_one") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) %3 = add i64 %2, 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %3, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr @"tracepoint:sched:sched_one", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") ret i64 1 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define i64 @tracepoint_sched_sched_two_2(ptr %0) section "s_tracepoint_sched_sched_two_2" !dbg !59 { entry: %"@x_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr @"tracepoint:sched:sched_two") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) %3 = add i64 %2, 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %3, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr @"tracepoint:sched:sched_two", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") ret i64 1 } attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!50} !llvm.module.flags = !{!52} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !22} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 216, elements: !20) !19 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !20 = !{!21} !21 = !DISubrange(count: 27, lowerBound: 0) !22 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !23, size: 64, offset: 192) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !32} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !20) !32 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !33, size: 64, offset: 64) !33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !34, size: 64) !34 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !35) !35 = !{!36} !36 = !DISubrange(count: 262144, lowerBound: 0) !37 = !DIGlobalVariableExpression(var: !38, expr: !DIExpression()) !38 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !39, isLocal: false, isDefinition: true) !39 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !40) !40 = !{!41, !46, !47, !22} !41 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !42, size: 64) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !44) !44 = !{!45} !45 = !DISubrange(count: 2, lowerBound: 0) !46 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !47 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !48, size: 64, offset: 128) !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !50 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !51) !51 = !{!0, !25, !37} !52 = !{i32 2, !"Debug Info Version", i32 3} !53 = distinct !DISubprogram(name: "tracepoint_sched_sched_one_1", linkageName: "tracepoint_sched_sched_one_1", scope: !2, file: !2, type: !54, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !50, retainedNodes: !57) !54 = !DISubroutineType(types: !55) !55 = !{!24, !56} !56 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) !57 = !{!58} !58 = !DILocalVariable(name: "ctx", arg: 1, scope: !53, file: !2, type: !56) !59 = distinct !DISubprogram(name: "tracepoint_sched_sched_two_2", linkageName: "tracepoint_sched_sched_two_2", scope: !2, file: !2, type: !54, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !50, retainedNodes: !60) !60 = !{!61} !61 = !DILocalVariable(name: "ctx", arg: 1, scope: !59, file: !2, type: !56) bpftrace-0.23.2/tests/codegen/llvm/map_key_scratch_buf.ll000066400000000000000000000232651477746507000234560ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !44 @map_key_buf = dso_local externally_initialized global [1 x [2 x [8 x i8]]] zeroinitializer, section ".data.map_key_buf", !dbg !57 @write_map_val_buf = dso_local externally_initialized global [1 x [1 x [8 x i8]]] zeroinitializer, section ".data.write_map_val_buf", !dbg !64 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !68 @read_map_val_buf = dso_local externally_initialized global [1 x [1 x [8 x i8]]] zeroinitializer, section ".data.read_map_val_buf", !dbg !70 @yyyy = global [5 x i8] c"yyyy\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !75 { entry: %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [2 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 store i64 1, ptr %2, align 8 %get_cpu_id1 = call i64 inttoptr (i64 8 to ptr)() %3 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded2 = and i64 %get_cpu_id1, %3 %4 = getelementptr [1 x [1 x [8 x i8]]], ptr @write_map_val_buf, i64 0, i64 %cpu.id.bounded2, i64 0, i64 0 store i64 1, ptr %4, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %2, ptr %4, i64 0) %get_cpu_id3 = call i64 inttoptr (i64 8 to ptr)() %5 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded4 = and i64 %get_cpu_id3, %5 %6 = getelementptr [1 x [2 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded4, i64 1, i64 0 store i64 1, ptr %6, align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %6) %get_cpu_id5 = call i64 inttoptr (i64 8 to ptr)() %7 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded6 = and i64 %get_cpu_id5, %7 %8 = getelementptr [1 x [1 x [8 x i8]]], ptr @read_map_val_buf, i64 0, i64 %cpu.id.bounded6, i64 0, i64 0 %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %9 = load i64, ptr %lookup_elem, align 8 store i64 %9, ptr %8, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %8, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %10 = load i64, ptr %8, align 8 %get_cpu_id7 = call i64 inttoptr (i64 8 to ptr)() %11 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded8 = and i64 %get_cpu_id7, %11 %12 = getelementptr [1 x [1 x [8 x i8]]], ptr @write_map_val_buf, i64 0, i64 %cpu.id.bounded8, i64 0, i64 0 store i64 %10, ptr %12, align 8 %update_elem9 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr @yyyy, ptr %12, i64 0) ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!72} !llvm.module.flags = !{!74} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !23) !23 = !{!5, !11, !24, !19} !24 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !25, size: 64, offset: 128) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !27, size: 40, elements: !28) !27 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !28 = !{!29} !29 = !DISubrange(count: 5, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !33) !33 = !{!34, !39} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 27, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !40, size: 64, offset: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 262144, lowerBound: 0) !44 = !DIGlobalVariableExpression(var: !45, expr: !DIExpression()) !45 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !46, isLocal: false, isDefinition: true) !46 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !47) !47 = !{!48, !53, !54, !19} !48 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !49, size: 64) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !51) !51 = !{!52} !52 = !DISubrange(count: 2, lowerBound: 0) !53 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !54 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !55, size: 64, offset: 128) !55 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !56, size: 64) !56 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !57 = !DIGlobalVariableExpression(var: !58, expr: !DIExpression()) !58 = distinct !DIGlobalVariable(name: "map_key_buf", linkageName: "global", scope: !2, file: !2, type: !59, isLocal: false, isDefinition: true) !59 = !DICompositeType(tag: DW_TAG_array_type, baseType: !60, size: 128, elements: !9) !60 = !DICompositeType(tag: DW_TAG_array_type, baseType: !61, size: 128, elements: !51) !61 = !DICompositeType(tag: DW_TAG_array_type, baseType: !27, size: 64, elements: !62) !62 = !{!63} !63 = !DISubrange(count: 8, lowerBound: 0) !64 = !DIGlobalVariableExpression(var: !65, expr: !DIExpression()) !65 = distinct !DIGlobalVariable(name: "write_map_val_buf", linkageName: "global", scope: !2, file: !2, type: !66, isLocal: false, isDefinition: true) !66 = !DICompositeType(tag: DW_TAG_array_type, baseType: !67, size: 64, elements: !9) !67 = !DICompositeType(tag: DW_TAG_array_type, baseType: !61, size: 64, elements: !9) !68 = !DIGlobalVariableExpression(var: !69, expr: !DIExpression()) !69 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !70 = !DIGlobalVariableExpression(var: !71, expr: !DIExpression()) !71 = distinct !DIGlobalVariable(name: "read_map_val_buf", linkageName: "global", scope: !2, file: !2, type: !66, isLocal: false, isDefinition: true) !72 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !73) !73 = !{!0, !20, !30, !44, !57, !64, !68, !70} !74 = !{i32 2, !"Debug Info Version", i32 3} !75 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !76, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !72, retainedNodes: !79) !76 = !DISubroutineType(types: !77) !77 = !{!18, !78} !78 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !79 = !{!80} !80 = !DILocalVariable(name: "ctx", arg: 1, scope: !75, file: !2, type: !78) bpftrace-0.23.2/tests/codegen/llvm/map_key_stack.ll000066400000000000000000000176131477746507000223000ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !44 @yyyy = global [5 x i8] c"yyyy\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !60 { entry: %"@y_val" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 1, ptr %"@x_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") store i64 %2, ptr %"@y_val", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr @yyyy, ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!57} !llvm.module.flags = !{!59} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !23) !23 = !{!5, !11, !24, !19} !24 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !25, size: 64, offset: 128) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !27, size: 40, elements: !28) !27 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !28 = !{!29} !29 = !DISubrange(count: 5, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !33) !33 = !{!34, !39} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 27, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !40, size: 64, offset: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 262144, lowerBound: 0) !44 = !DIGlobalVariableExpression(var: !45, expr: !DIExpression()) !45 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !46, isLocal: false, isDefinition: true) !46 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !47) !47 = !{!48, !53, !54, !19} !48 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !49, size: 64) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !51) !51 = !{!52} !52 = !DISubrange(count: 2, lowerBound: 0) !53 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !54 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !55, size: 64, offset: 128) !55 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !56, size: 64) !56 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !57 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !58) !58 = !{!0, !20, !30, !44} !59 = !{i32 2, !"Debug Info Version", i32 3} !60 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !57, retainedNodes: !64) !61 = !DISubroutineType(types: !62) !62 = !{!18, !63} !63 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !64 = !{!65} !65 = !DILocalVariable(name: "ctx", arg: 1, scope: !60, file: !2, type: !63) bpftrace-0.23.2/tests/codegen/llvm/map_key_string.ll000066400000000000000000000164261477746507000225020ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"string[2]_string[2]__tuple_t" = type { [2 x i8], [2 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !29 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !43 @a = global [2 x i8] c"a\00" @b = global [2 x i8] c"b\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %"@x_val" = alloca i64, align 8 %tuple = alloca %"string[2]_string[2]__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 4, i1 false) %1 = getelementptr %"string[2]_string[2]__tuple_t", ptr %tuple, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %1, ptr align 1 @a, i64 2, i1 false) %2 = getelementptr %"string[2]_string[2]__tuple_t", ptr %tuple, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %2, ptr align 1 @b, i64 2, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 44, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %tuple, ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !26} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 32, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 16) !21 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 16, elements: !23) !22 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 16, offset: 16) !26 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !27, size: 64, offset: 192) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !29 = !DIGlobalVariableExpression(var: !30, expr: !DIExpression()) !30 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !31, isLocal: false, isDefinition: true) !31 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !32) !32 = !{!33, !38} !33 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !34, size: 64) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !36) !36 = !{!37} !37 = !DISubrange(count: 27, lowerBound: 0) !38 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !39, size: 64, offset: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 262144, lowerBound: 0) !43 = !DIGlobalVariableExpression(var: !44, expr: !DIExpression()) !44 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !45, isLocal: false, isDefinition: true) !45 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !46) !46 = !{!47, !50, !51, !26} !47 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !48, size: 64) !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !50 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !51 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !52, size: 64, offset: 128) !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !53, size: 64) !53 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !29, !43} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !61) !58 = !DISubroutineType(types: !59) !59 = !{!28, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/map_key_struct.ll000066400000000000000000000146761477746507000225250ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !55 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca [4 x i8], align 1 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"@x_key", i32 4, i64 %arg0) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 44, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!52} !llvm.module.flags = !{!54} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !22} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 32, elements: !20) !19 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !20 = !{!21} !21 = !DISubrange(count: 4, lowerBound: 0) !22 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !23, size: 64, offset: 192) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !48, !49, !22} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !52 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !53) !53 = !{!0, !25, !39} !54 = !{i32 2, !"Debug Info Version", i32 3} !55 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !52, retainedNodes: !59) !56 = !DISubroutineType(types: !57) !57 = !{!24, !58} !58 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !55, file: !2, type: !58) bpftrace-0.23.2/tests/codegen/llvm/map_value_int_scratch_buf.ll000066400000000000000000000223061477746507000246470ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 @map_key_buf = dso_local externally_initialized global [1 x [3 x [8 x i8]]] zeroinitializer, section ".data.map_key_buf", !dbg !44 @write_map_val_buf = dso_local externally_initialized global [1 x [1 x [8 x i8]]] zeroinitializer, section ".data.write_map_val_buf", !dbg !54 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !58 @read_map_val_buf = dso_local externally_initialized global [1 x [1 x [8 x i8]]] zeroinitializer, section ".data.read_map_val_buf", !dbg !60 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !65 { entry: %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [3 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 store i64 0, ptr %2, align 8 %get_cpu_id1 = call i64 inttoptr (i64 8 to ptr)() %3 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded2 = and i64 %get_cpu_id1, %3 %4 = getelementptr [1 x [1 x [8 x i8]]], ptr @write_map_val_buf, i64 0, i64 %cpu.id.bounded2, i64 0, i64 0 store i64 1, ptr %4, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %2, ptr %4, i64 0) %get_cpu_id3 = call i64 inttoptr (i64 8 to ptr)() %5 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded4 = and i64 %get_cpu_id3, %5 %6 = getelementptr [1 x [3 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded4, i64 1, i64 0 store i64 0, ptr %6, align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %6) %get_cpu_id5 = call i64 inttoptr (i64 8 to ptr)() %7 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded6 = and i64 %get_cpu_id5, %7 %8 = getelementptr [1 x [1 x [8 x i8]]], ptr @read_map_val_buf, i64 0, i64 %cpu.id.bounded6, i64 0, i64 0 %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %9 = load i64, ptr %lookup_elem, align 8 store i64 %9, ptr %8, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %8, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %10 = load i64, ptr %8, align 8 %get_cpu_id7 = call i64 inttoptr (i64 8 to ptr)() %11 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded8 = and i64 %get_cpu_id7, %11 %12 = getelementptr [1 x [3 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded8, i64 2, i64 0 store i64 0, ptr %12, align 8 %get_cpu_id9 = call i64 inttoptr (i64 8 to ptr)() %13 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded10 = and i64 %get_cpu_id9, %13 %14 = getelementptr [1 x [1 x [8 x i8]]], ptr @write_map_val_buf, i64 0, i64 %cpu.id.bounded10, i64 0, i64 0 store i64 %10, ptr %14, align 8 %update_elem11 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %12, ptr %14, i64 0) ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!62} !llvm.module.flags = !{!64} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = !DIGlobalVariableExpression(var: !45, expr: !DIExpression()) !45 = distinct !DIGlobalVariable(name: "map_key_buf", linkageName: "global", scope: !2, file: !2, type: !46, isLocal: false, isDefinition: true) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !47, size: 192, elements: !9) !47 = !DICompositeType(tag: DW_TAG_array_type, baseType: !48, size: 192, elements: !52) !48 = !DICompositeType(tag: DW_TAG_array_type, baseType: !49, size: 64, elements: !50) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DISubrange(count: 8, lowerBound: 0) !52 = !{!53} !53 = !DISubrange(count: 3, lowerBound: 0) !54 = !DIGlobalVariableExpression(var: !55, expr: !DIExpression()) !55 = distinct !DIGlobalVariable(name: "write_map_val_buf", linkageName: "global", scope: !2, file: !2, type: !56, isLocal: false, isDefinition: true) !56 = !DICompositeType(tag: DW_TAG_array_type, baseType: !57, size: 64, elements: !9) !57 = !DICompositeType(tag: DW_TAG_array_type, baseType: !48, size: 64, elements: !9) !58 = !DIGlobalVariableExpression(var: !59, expr: !DIExpression()) !59 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !60 = !DIGlobalVariableExpression(var: !61, expr: !DIExpression()) !61 = distinct !DIGlobalVariable(name: "read_map_val_buf", linkageName: "global", scope: !2, file: !2, type: !56, isLocal: false, isDefinition: true) !62 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !63) !63 = !{!0, !16, !18, !32, !44, !54, !58, !60} !64 = !{i32 2, !"Debug Info Version", i32 3} !65 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !66, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !62, retainedNodes: !69) !66 = !DISubroutineType(types: !67) !67 = !{!14, !68} !68 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !69 = !{!70} !70 = !DILocalVariable(name: "ctx", arg: 1, scope: !65, file: !2, type: !68) bpftrace-0.23.2/tests/codegen/llvm/map_value_int_stack.ll000066400000000000000000000164151477746507000234750ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") store i64 %2, ptr %"@y_val", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/map_value_tuple_scratch_buf.ll000066400000000000000000000313741477746507000252130ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %"string[4]_int64__tuple_t" = type { [4 x i8], i64 } %"string[8]_int64__tuple_t" = type { [8 x i8], i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !27 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !41 @map_key_buf = dso_local externally_initialized global [1 x [4 x [8 x i8]]] zeroinitializer, section ".data.map_key_buf", !dbg !54 @write_map_val_buf = dso_local externally_initialized global [1 x [1 x [16 x i8]]] zeroinitializer, section ".data.write_map_val_buf", !dbg !60 @read_map_val_buf = dso_local externally_initialized global [1 x [1 x [16 x i8]]] zeroinitializer, section ".data.read_map_val_buf", !dbg !67 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !69 @tuple_buf = dso_local externally_initialized global [1 x [2 x [16 x i8]]] zeroinitializer, section ".data.tuple_buf", !dbg !71 @xxx = global [4 x i8] c"xxx\00" @xxxxxxx = global [8 x i8] c"xxxxxxx\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !78 { entry: %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [2 x [16 x i8]]], ptr @tuple_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 call void @llvm.memset.p0.i64(ptr align 1 %2, i8 0, i64 16, i1 false) %3 = getelementptr %"string[4]_int64__tuple_t", ptr %2, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %3, ptr align 1 @xxx, i64 4, i1 false) %4 = getelementptr %"string[4]_int64__tuple_t", ptr %2, i32 0, i32 1 store i64 1, ptr %4, align 8 %get_cpu_id1 = call i64 inttoptr (i64 8 to ptr)() %5 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded2 = and i64 %get_cpu_id1, %5 %6 = getelementptr [1 x [4 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded2, i64 0, i64 0 store i64 0, ptr %6, align 8 %get_cpu_id3 = call i64 inttoptr (i64 8 to ptr)() %7 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded4 = and i64 %get_cpu_id3, %7 %8 = getelementptr [1 x [1 x [16 x i8]]], ptr @write_map_val_buf, i64 0, i64 %cpu.id.bounded4, i64 0, i64 0 call void @llvm.memset.p0.i64(ptr align 1 %8, i8 0, i64 16, i1 false) %9 = getelementptr [16 x i8], ptr %2, i64 0, i64 0 %10 = getelementptr %"string[8]_int64__tuple_t", ptr %8, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %10, ptr align 1 %9, i64 4, i1 false) %11 = getelementptr [16 x i8], ptr %2, i64 0, i64 8 %12 = getelementptr %"string[8]_int64__tuple_t", ptr %8, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %12, ptr align 1 %11, i64 8, i1 false) %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %6, ptr %8, i64 0) %get_cpu_id5 = call i64 inttoptr (i64 8 to ptr)() %13 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded6 = and i64 %get_cpu_id5, %13 %14 = getelementptr [1 x [2 x [16 x i8]]], ptr @tuple_buf, i64 0, i64 %cpu.id.bounded6, i64 1, i64 0 call void @llvm.memset.p0.i64(ptr align 1 %14, i8 0, i64 16, i1 false) %15 = getelementptr %"string[8]_int64__tuple_t", ptr %14, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %15, ptr align 1 @xxxxxxx, i64 8, i1 false) %16 = getelementptr %"string[8]_int64__tuple_t", ptr %14, i32 0, i32 1 store i64 1, ptr %16, align 8 %get_cpu_id7 = call i64 inttoptr (i64 8 to ptr)() %17 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded8 = and i64 %get_cpu_id7, %17 %18 = getelementptr [1 x [4 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded8, i64 1, i64 0 store i64 0, ptr %18, align 8 %update_elem9 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %18, ptr %14, i64 0) %get_cpu_id10 = call i64 inttoptr (i64 8 to ptr)() %19 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded11 = and i64 %get_cpu_id10, %19 %20 = getelementptr [1 x [4 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded11, i64 2, i64 0 store i64 0, ptr %20, align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %20) %get_cpu_id12 = call i64 inttoptr (i64 8 to ptr)() %21 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded13 = and i64 %get_cpu_id12, %21 %22 = getelementptr [1 x [1 x [16 x i8]]], ptr @read_map_val_buf, i64 0, i64 %cpu.id.bounded13, i64 0, i64 0 %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.memcpy.p0.p0.i64(ptr align 1 %22, ptr align 1 %lookup_elem, i64 16, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.memset.p0.i64(ptr align 1 %22, i8 0, i64 16, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %get_cpu_id14 = call i64 inttoptr (i64 8 to ptr)() %23 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded15 = and i64 %get_cpu_id14, %23 %24 = getelementptr [1 x [4 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded15, i64 3, i64 0 store i64 0, ptr %24, align 8 %update_elem16 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %24, ptr %22, i64 0) ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!75} !llvm.module.flags = !{!77} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !18) !18 = !{!19, !24} !19 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !20, size: 64) !20 = !DICompositeType(tag: DW_TAG_array_type, baseType: !21, size: 64, elements: !22) !21 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !22 = !{!23} !23 = !DISubrange(count: 8, lowerBound: 0) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !14, size: 64, offset: 64) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !27 = !DIGlobalVariableExpression(var: !28, expr: !DIExpression()) !28 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !29, isLocal: false, isDefinition: true) !29 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !30) !30 = !{!31, !36} !31 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !32, size: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 27, lowerBound: 0) !36 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !37, size: 64, offset: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 262144, lowerBound: 0) !41 = !DIGlobalVariableExpression(var: !42, expr: !DIExpression()) !42 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !43, isLocal: false, isDefinition: true) !43 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !44) !44 = !{!45, !11, !50, !53} !45 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !46, size: 64) !46 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !47, size: 64) !47 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !48) !48 = !{!49} !49 = !DISubrange(count: 2, lowerBound: 0) !50 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !51, size: 64, offset: 128) !51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !52, size: 64) !52 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !53 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !54 = !DIGlobalVariableExpression(var: !55, expr: !DIExpression()) !55 = distinct !DIGlobalVariable(name: "map_key_buf", linkageName: "global", scope: !2, file: !2, type: !56, isLocal: false, isDefinition: true) !56 = !DICompositeType(tag: DW_TAG_array_type, baseType: !57, size: 256, elements: !9) !57 = !DICompositeType(tag: DW_TAG_array_type, baseType: !20, size: 256, elements: !58) !58 = !{!59} !59 = !DISubrange(count: 4, lowerBound: 0) !60 = !DIGlobalVariableExpression(var: !61, expr: !DIExpression()) !61 = distinct !DIGlobalVariable(name: "write_map_val_buf", linkageName: "global", scope: !2, file: !2, type: !62, isLocal: false, isDefinition: true) !62 = !DICompositeType(tag: DW_TAG_array_type, baseType: !63, size: 128, elements: !9) !63 = !DICompositeType(tag: DW_TAG_array_type, baseType: !64, size: 128, elements: !9) !64 = !DICompositeType(tag: DW_TAG_array_type, baseType: !21, size: 128, elements: !65) !65 = !{!66} !66 = !DISubrange(count: 16, lowerBound: 0) !67 = !DIGlobalVariableExpression(var: !68, expr: !DIExpression()) !68 = distinct !DIGlobalVariable(name: "read_map_val_buf", linkageName: "global", scope: !2, file: !2, type: !62, isLocal: false, isDefinition: true) !69 = !DIGlobalVariableExpression(var: !70, expr: !DIExpression()) !70 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !71 = !DIGlobalVariableExpression(var: !72, expr: !DIExpression()) !72 = distinct !DIGlobalVariable(name: "tuple_buf", linkageName: "global", scope: !2, file: !2, type: !73, isLocal: false, isDefinition: true) !73 = !DICompositeType(tag: DW_TAG_array_type, baseType: !74, size: 256, elements: !9) !74 = !DICompositeType(tag: DW_TAG_array_type, baseType: !64, size: 256, elements: !48) !75 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !76) !76 = !{!0, !25, !27, !41, !54, !60, !67, !69, !71} !77 = !{i32 2, !"Debug Info Version", i32 3} !78 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !79, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !75, retainedNodes: !82) !79 = !DISubroutineType(types: !80) !80 = !{!14, !81} !81 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !82 = !{!83} !83 = !DILocalVariable(name: "ctx", arg: 1, scope: !78, file: !2, type: !81) bpftrace-0.23.2/tests/codegen/llvm/map_value_tuple_stack.ll000066400000000000000000000243531477746507000240340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %"string[8]_int64__tuple_t" = type { [8 x i8], i64 } %"string[4]_int64__tuple_t" = type { [4 x i8], i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !27 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !41 @xxx = global [4 x i8] c"xxx\00" @xxxxxxx = global [8 x i8] c"xxxxxxx\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %"@y_key" = alloca i64, align 8 %lookup_elem_val = alloca %"string[8]_int64__tuple_t", align 8 %"@x_key4" = alloca i64, align 8 %"@x_key2" = alloca i64, align 8 %tuple1 = alloca %"string[8]_int64__tuple_t", align 8 %"@x_val" = alloca %"string[8]_int64__tuple_t", align 8 %"@x_key" = alloca i64, align 8 %tuple = alloca %"string[4]_int64__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 16, i1 false) %1 = getelementptr %"string[4]_int64__tuple_t", ptr %tuple, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %1, ptr align 1 @xxx, i64 4, i1 false) %2 = getelementptr %"string[4]_int64__tuple_t", ptr %tuple, i32 0, i32 1 store i64 1, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") call void @llvm.memset.p0.i64(ptr align 1 %"@x_val", i8 0, i64 16, i1 false) %3 = getelementptr [16 x i8], ptr %tuple, i64 0, i64 0 %4 = getelementptr %"string[8]_int64__tuple_t", ptr %"@x_val", i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %4, ptr align 1 %3, i64 4, i1 false) %5 = getelementptr [16 x i8], ptr %tuple, i64 0, i64 8 %6 = getelementptr %"string[8]_int64__tuple_t", ptr %"@x_val", i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %5, i64 8, i1 false) %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple1) call void @llvm.memset.p0.i64(ptr align 1 %tuple1, i8 0, i64 16, i1 false) %7 = getelementptr %"string[8]_int64__tuple_t", ptr %tuple1, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %7, ptr align 1 @xxxxxxx, i64 8, i1 false) %8 = getelementptr %"string[8]_int64__tuple_t", ptr %tuple1, i32 0, i32 1 store i64 1, ptr %8, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key2") store i64 0, ptr %"@x_key2", align 8 %update_elem3 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key2", ptr %tuple1, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key2") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple1) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key4") store i64 0, ptr %"@x_key4", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key4") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.memcpy.p0.p0.i64(ptr align 1 %lookup_elem_val, ptr align 1 %lookup_elem, i64 16, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.memset.p0.i64(ptr align 1 %lookup_elem_val, i8 0, i64 16, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key4") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 %update_elem5 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %lookup_elem_val, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !18) !18 = !{!19, !24} !19 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !20, size: 64) !20 = !DICompositeType(tag: DW_TAG_array_type, baseType: !21, size: 64, elements: !22) !21 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !22 = !{!23} !23 = !DISubrange(count: 8, lowerBound: 0) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !14, size: 64, offset: 64) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !27 = !DIGlobalVariableExpression(var: !28, expr: !DIExpression()) !28 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !29, isLocal: false, isDefinition: true) !29 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !30) !30 = !{!31, !36} !31 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !32, size: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 27, lowerBound: 0) !36 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !37, size: 64, offset: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 262144, lowerBound: 0) !41 = !DIGlobalVariableExpression(var: !42, expr: !DIExpression()) !42 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !43, isLocal: false, isDefinition: true) !43 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !44) !44 = !{!45, !11, !50, !53} !45 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !46, size: 64) !46 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !47, size: 64) !47 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !48) !48 = !{!49} !49 = !DISubrange(count: 2, lowerBound: 0) !50 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !51, size: 64, offset: 128) !51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !52, size: 64) !52 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !53 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !25, !27, !41} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !61) !58 = !DISubroutineType(types: !59) !59 = !{!14, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/max_cast.ll000066400000000000000000000261171477746507000212640ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %min_max_val = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !52 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %"@x_key1" = alloca i64, align 8 %mm_struct = alloca %min_max_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 %2 = load i64, ptr %1, align 8 %3 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 %4 = load i64, ptr %3, align 8 %is_set_cond = icmp eq i64 %4, 1 br i1 %is_set_cond, label %is_set, label %min_max lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %mm_struct) %5 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 0 store i64 2, ptr %5, align 8 %6 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 1 store i64 1, ptr %6, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %mm_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %mm_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %min_max, %is_set call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond is_set: ; preds = %lookup_success %7 = icmp sge i64 2, %2 br i1 %7, label %min_max, label %lookup_merge min_max: ; preds = %is_set, %lookup_success %8 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 store i64 2, ptr %8, align 8 %9 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 store i64 1, ptr %9, align 8 br label %lookup_merge while_cond: ; preds = %min_max_merge, %lookup_merge %10 = load i32, ptr @num_cpus, align 4 %11 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %11, %10 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %12 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %"@x_key1", i32 %12) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success2, label %lookup_failure3 while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %13 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") store i64 %13, ptr %"$res", align 8 ret i64 0 lookup_success2: ; preds = %while_body %14 = getelementptr %min_max_val, ptr %lookup_percpu_elem, i64 0, i32 0 %15 = load i64, ptr %14, align 8 %16 = getelementptr %min_max_val, ptr %lookup_percpu_elem, i64 0, i32 1 %17 = load i64, ptr %16, align 8 %val_set_cond = icmp eq i64 %17, 1 %18 = load i64, ptr %val_2, align 8 %ret_set_cond = icmp eq i64 %18, 1 %19 = load i64, ptr %val_1, align 8 %max_cond = icmp sgt i64 %15, %19 br i1 %val_set_cond, label %val_set_success, label %min_max_merge lookup_failure3: ; preds = %while_body %20 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %20, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure val_set_success: ; preds = %lookup_success2 br i1 %ret_set_cond, label %ret_set_success, label %min_max_success min_max_success: ; preds = %ret_set_success, %val_set_success store i64 %15, ptr %val_1, align 8 store i64 1, ptr %val_2, align 8 br label %min_max_merge ret_set_success: ; preds = %val_set_success br i1 %max_cond, label %min_max_success, label %min_max_merge min_max_merge: ; preds = %min_max_success, %ret_set_success, %lookup_success2 %21 = load i32, ptr %i, align 4 %22 = add i32 %21, 1 store i32 %22, ptr %i, align 4 br label %while_cond error_success: ; preds = %lookup_failure3 br label %while_end error_failure: ; preds = %lookup_failure3 %23 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !51} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !51 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !52 = !DIGlobalVariableExpression(var: !53, expr: !DIExpression()) !53 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !26, !40, !52} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !62) !58 = !DISubroutineType(types: !59) !59 = !{!18, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !61, size: 64) !61 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !62 = !{!63} !63 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/max_cast_loop.ll000066400000000000000000000315141477746507000223120ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %min_max_val = type { i64, i64 } %int64_max_t__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !57 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !62 { entry: %mm_struct = alloca %min_max_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 %2 = load i64, ptr %1, align 8 %3 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 %4 = load i64, ptr %3, align 8 %is_set_cond = icmp eq i64 %4, 1 br i1 %is_set_cond, label %is_set, label %min_max lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %mm_struct) %5 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 0 store i64 2, ptr %5, align 8 %6 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 1 store i64 1, ptr %6, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %mm_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %mm_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %min_max, %is_set call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_x, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 is_set: ; preds = %lookup_success %7 = icmp sge i64 2, %2 br i1 %7, label %min_max, label %lookup_merge min_max: ; preds = %is_set, %lookup_success %8 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 store i64 2, ptr %8, align 8 %9 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 store i64 1, ptr %9, align 8 br label %lookup_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !69 { %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %"$kv" = alloca %int64_max_t__tuple_t, align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %key = load i64, ptr %1, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %min_max_merge, %4 %5 = load i32, ptr @num_cpus, align 4 %6 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %6, %5 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %7 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %1, i32 %7) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %8 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %9 = getelementptr %int64_max_t__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %9, align 8 %10 = getelementptr %int64_max_t__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %8, ptr %10, align 8 %11 = getelementptr %int64_max_t__tuple_t, ptr %"$kv", i32 0, i32 1 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %"$res", align 8 ret i64 0 lookup_success: ; preds = %while_body %13 = getelementptr %min_max_val, ptr %lookup_percpu_elem, i64 0, i32 0 %14 = load i64, ptr %13, align 8 %15 = getelementptr %min_max_val, ptr %lookup_percpu_elem, i64 0, i32 1 %16 = load i64, ptr %15, align 8 %val_set_cond = icmp eq i64 %16, 1 %17 = load i64, ptr %val_2, align 8 %ret_set_cond = icmp eq i64 %17, 1 %18 = load i64, ptr %val_1, align 8 %max_cond = icmp sgt i64 %14, %18 br i1 %val_set_cond, label %val_set_success, label %min_max_merge lookup_failure: ; preds = %while_body %19 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %19, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure val_set_success: ; preds = %lookup_success br i1 %ret_set_cond, label %ret_set_success, label %min_max_success min_max_success: ; preds = %ret_set_success, %val_set_success store i64 %14, ptr %val_1, align 8 store i64 1, ptr %val_2, align 8 br label %min_max_merge ret_set_success: ; preds = %val_set_success br i1 %max_cond, label %min_max_success, label %min_max_merge min_max_merge: ; preds = %min_max_success, %ret_set_success, %lookup_success %20 = load i32, ptr %i, align 4 %21 = add i32 %20, 1 store i32 %21, ptr %i, align 4 br label %while_cond error_success: ; preds = %lookup_failure br label %while_end error_failure: ; preds = %lookup_failure %22 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!59} !llvm.module.flags = !{!61} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !49, !54, !56} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !50, size: 64, offset: 64) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !52) !52 = !{!53} !53 = !DISubrange(count: 1, lowerBound: 0) !54 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !55, size: 64, offset: 128) !55 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !56 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !57 = !DIGlobalVariableExpression(var: !58, expr: !DIExpression()) !58 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !59 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !60) !60 = !{!0, !26, !40, !57} !61 = !{i32 2, !"Debug Info Version", i32 3} !62 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !59, retainedNodes: !67) !63 = !DISubroutineType(types: !64) !64 = !{!18, !65} !65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) !66 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !67 = !{!68} !68 = !DILocalVariable(name: "ctx", arg: 1, scope: !62, file: !2, type: !65) !69 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !70, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !59, retainedNodes: !72) !70 = !DISubroutineType(types: !71) !71 = !{!18, !65, !65, !65, !65} !72 = !{!73, !74, !75, !76} !73 = !DILocalVariable(name: "map", arg: 1, scope: !69, file: !2, type: !65) !74 = !DILocalVariable(name: "key", arg: 2, scope: !69, file: !2, type: !65) !75 = !DILocalVariable(name: "value", arg: 3, scope: !69, file: !2, type: !65) !76 = !DILocalVariable(name: "ctx", arg: 4, scope: !69, file: !2, type: !65) bpftrace-0.23.2/tests/codegen/llvm/min_cast.ll000066400000000000000000000261171477746507000212620ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %min_max_val = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !52 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %"@x_key1" = alloca i64, align 8 %mm_struct = alloca %min_max_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 %2 = load i64, ptr %1, align 8 %3 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 %4 = load i64, ptr %3, align 8 %is_set_cond = icmp eq i64 %4, 1 br i1 %is_set_cond, label %is_set, label %min_max lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %mm_struct) %5 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 0 store i64 2, ptr %5, align 8 %6 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 1 store i64 1, ptr %6, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %mm_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %mm_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %min_max, %is_set call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond is_set: ; preds = %lookup_success %7 = icmp sge i64 %2, 2 br i1 %7, label %min_max, label %lookup_merge min_max: ; preds = %is_set, %lookup_success %8 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 store i64 2, ptr %8, align 8 %9 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 store i64 1, ptr %9, align 8 br label %lookup_merge while_cond: ; preds = %min_max_merge, %lookup_merge %10 = load i32, ptr @num_cpus, align 4 %11 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %11, %10 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %12 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %"@x_key1", i32 %12) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success2, label %lookup_failure3 while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %13 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") store i64 %13, ptr %"$res", align 8 ret i64 0 lookup_success2: ; preds = %while_body %14 = getelementptr %min_max_val, ptr %lookup_percpu_elem, i64 0, i32 0 %15 = load i64, ptr %14, align 8 %16 = getelementptr %min_max_val, ptr %lookup_percpu_elem, i64 0, i32 1 %17 = load i64, ptr %16, align 8 %val_set_cond = icmp eq i64 %17, 1 %18 = load i64, ptr %val_2, align 8 %ret_set_cond = icmp eq i64 %18, 1 %19 = load i64, ptr %val_1, align 8 %min_cond = icmp slt i64 %15, %19 br i1 %val_set_cond, label %val_set_success, label %min_max_merge lookup_failure3: ; preds = %while_body %20 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %20, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure val_set_success: ; preds = %lookup_success2 br i1 %ret_set_cond, label %ret_set_success, label %min_max_success min_max_success: ; preds = %ret_set_success, %val_set_success store i64 %15, ptr %val_1, align 8 store i64 1, ptr %val_2, align 8 br label %min_max_merge ret_set_success: ; preds = %val_set_success br i1 %min_cond, label %min_max_success, label %min_max_merge min_max_merge: ; preds = %min_max_success, %ret_set_success, %lookup_success2 %21 = load i32, ptr %i, align 4 %22 = add i32 %21, 1 store i32 %22, ptr %i, align 4 br label %while_cond error_success: ; preds = %lookup_failure3 br label %while_end error_failure: ; preds = %lookup_failure3 %23 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !51} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !51 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !52 = !DIGlobalVariableExpression(var: !53, expr: !DIExpression()) !53 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !26, !40, !52} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !62) !58 = !DISubroutineType(types: !59) !59 = !{!18, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !61, size: 64) !61 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !62 = !{!63} !63 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/min_cast_loop.ll000066400000000000000000000315141477746507000223100ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %min_max_val = type { i64, i64 } %int64_min_t__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !57 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !62 { entry: %mm_struct = alloca %min_max_val, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") %lookup_cond = icmp ne ptr %lookup_elem, null br i1 %lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 %2 = load i64, ptr %1, align 8 %3 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 %4 = load i64, ptr %3, align 8 %is_set_cond = icmp eq i64 %4, 1 br i1 %is_set_cond, label %is_set, label %min_max lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %mm_struct) %5 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 0 store i64 2, ptr %5, align 8 %6 = getelementptr %min_max_val, ptr %mm_struct, i64 0, i32 1 store i64 1, ptr %6, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %mm_struct, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %mm_struct) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %min_max, %is_set call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_x, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 is_set: ; preds = %lookup_success %7 = icmp sge i64 %2, 2 br i1 %7, label %min_max, label %lookup_merge min_max: ; preds = %is_set, %lookup_success %8 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 0 store i64 2, ptr %8, align 8 %9 = getelementptr %min_max_val, ptr %lookup_elem, i64 0, i32 1 store i64 1, ptr %9, align 8 br label %lookup_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !69 { %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %"$kv" = alloca %int64_min_t__tuple_t, align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %key = load i64, ptr %1, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %min_max_merge, %4 %5 = load i32, ptr @num_cpus, align 4 %6 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %6, %5 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %7 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %1, i32 %7) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %8 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %9 = getelementptr %int64_min_t__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %9, align 8 %10 = getelementptr %int64_min_t__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %8, ptr %10, align 8 %11 = getelementptr %int64_min_t__tuple_t, ptr %"$kv", i32 0, i32 1 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %"$res", align 8 ret i64 0 lookup_success: ; preds = %while_body %13 = getelementptr %min_max_val, ptr %lookup_percpu_elem, i64 0, i32 0 %14 = load i64, ptr %13, align 8 %15 = getelementptr %min_max_val, ptr %lookup_percpu_elem, i64 0, i32 1 %16 = load i64, ptr %15, align 8 %val_set_cond = icmp eq i64 %16, 1 %17 = load i64, ptr %val_2, align 8 %ret_set_cond = icmp eq i64 %17, 1 %18 = load i64, ptr %val_1, align 8 %min_cond = icmp slt i64 %14, %18 br i1 %val_set_cond, label %val_set_success, label %min_max_merge lookup_failure: ; preds = %while_body %19 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %19, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure val_set_success: ; preds = %lookup_success br i1 %ret_set_cond, label %ret_set_success, label %min_max_success min_max_success: ; preds = %ret_set_success, %val_set_success store i64 %14, ptr %val_1, align 8 store i64 1, ptr %val_2, align 8 br label %min_max_merge ret_set_success: ; preds = %val_set_success br i1 %min_cond, label %min_max_success, label %min_max_merge min_max_merge: ; preds = %min_max_success, %ret_set_success, %lookup_success %20 = load i32, ptr %i, align 4 %21 = add i32 %20, 1 store i32 %21, ptr %i, align 4 br label %while_cond error_success: ; preds = %lookup_failure br label %while_end error_failure: ; preds = %lookup_failure %22 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!59} !llvm.module.flags = !{!61} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !22) !22 = !{!23, !24} !23 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 64, offset: 64) !25 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !49, !54, !56} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !50, size: 64, offset: 64) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !52) !52 = !{!53} !53 = !DISubrange(count: 1, lowerBound: 0) !54 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !55, size: 64, offset: 128) !55 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) !56 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !57 = !DIGlobalVariableExpression(var: !58, expr: !DIExpression()) !58 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !59 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !60) !60 = !{!0, !26, !40, !57} !61 = !{i32 2, !"Debug Info Version", i32 3} !62 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !59, retainedNodes: !67) !63 = !DISubroutineType(types: !64) !64 = !{!18, !65} !65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) !66 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !67 = !{!68} !68 = !DILocalVariable(name: "ctx", arg: 1, scope: !62, file: !2, type: !65) !69 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !70, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !59, retainedNodes: !72) !70 = !DISubroutineType(types: !71) !71 = !{!18, !65, !65, !65, !65} !72 = !{!73, !74, !75, !76} !73 = !DILocalVariable(name: "map", arg: 1, scope: !69, file: !2, type: !65) !74 = !DILocalVariable(name: "key", arg: 2, scope: !69, file: !2, type: !65) !75 = !DILocalVariable(name: "value", arg: 3, scope: !69, file: !2, type: !65) !76 = !DILocalVariable(name: "ctx", arg: 4, scope: !69, file: !2, type: !65) bpftrace-0.23.2/tests/codegen/llvm/multiple_identical_probes.ll000066400000000000000000000104501477746507000246770ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: ret i64 0 } define i64 @kprobe_f_2(ptr %0) section "s_kprobe_f_2" !dbg !46 { entry: ret i64 0 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) !46 = distinct !DISubprogram(name: "kprobe_f_2", linkageName: "kprobe_f_2", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !47) !47 = !{!48} !48 = !DILocalVariable(name: "ctx", arg: 1, scope: !46, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/nested_array_struct.ll000066400000000000000000000231721477746507000235470ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_bar = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !49 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !62 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %lookup_elem_val = alloca [2 x [2 x [4 x i8]]], align 1 %"@bar_key1" = alloca i64, align 8 %"@bar_val" = alloca [2 x [2 x [4 x i8]]], align 1 %"@bar_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = inttoptr i64 %arg0 to ptr %4 = call ptr @llvm.preserve.static.offset(ptr %3) %5 = getelementptr i8, ptr %4, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@bar_key") store i64 42, ptr %"@bar_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@bar_val") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"@bar_val", i32 16, ptr %5) %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_bar, ptr %"@bar_key", ptr %"@bar_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@bar_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@bar_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@bar_key1") store i64 42, ptr %"@bar_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_bar, ptr %"@bar_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.memcpy.p0.p0.i64(ptr align 1 %lookup_elem_val, ptr align 1 %lookup_elem, i64 16, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.memset.p0.i64(ptr align 1 %lookup_elem_val, i8 0, i64 16, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@bar_key1") %6 = getelementptr [2 x [2 x [4 x i8]]], ptr %lookup_elem_val, i32 0, i64 0 %7 = getelementptr [2 x [4 x i8]], ptr %6, i32 0, i64 1 %8 = getelementptr [4 x i8], ptr %7, i32 0, i64 0 %9 = load volatile i32, ptr %8, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") %10 = sext i32 %9 to i64 store i64 %10, ptr %"@_val", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #4 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!59} !llvm.module.flags = !{!61} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_bar", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!5, !20, !12, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !21, size: 64, offset: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 4096, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !26, size: 64, offset: 192) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !28, size: 128, elements: !33) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !29, size: 64, elements: !33) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !30, size: 32, elements: !31) !30 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !31 = !{!32} !32 = !DISubrange(count: 4, lowerBound: 0) !33 = !{!34} !34 = !DISubrange(count: 2, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !38) !38 = !{!39, !44} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 27, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !45, size: 64, offset: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 262144, lowerBound: 0) !49 = !DIGlobalVariableExpression(var: !50, expr: !DIExpression()) !50 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !51, isLocal: false, isDefinition: true) !51 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !52) !52 = !{!53, !11, !56, !15} !53 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !54, size: 64) !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !33) !56 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !57, size: 64, offset: 128) !57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !58, size: 64) !58 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !59 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !60) !60 = !{!0, !16, !35, !49} !61 = !{i32 2, !"Debug Info Version", i32 3} !62 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !59, retainedNodes: !66) !63 = !DISubroutineType(types: !64) !64 = !{!14, !65} !65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64) !66 = !{!67} !67 = !DILocalVariable(name: "ctx", arg: 1, scope: !62, file: !2, type: !65) bpftrace-0.23.2/tests/codegen/llvm/nested_tuple_different_sizes.ll000066400000000000000000000206541477746507000254230ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"int64_(string[13],int64)__tuple_t" = type { i64, %"string[13]_int64__tuple_t" } %"string[13]_int64__tuple_t" = type { [13 x i8], i64 } %"int64_(string[3],int64)__tuple_t" = type { i64, %"string[3]_int64__tuple_t" } %"string[3]_int64__tuple_t" = type { [3 x i8], i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @hi = global [3 x i8] c"hi\00" @hellolongstr = global [13 x i8] c"hellolongstr\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %tuple3 = alloca %"int64_(string[13],int64)__tuple_t", align 8 %tuple2 = alloca %"string[13]_int64__tuple_t", align 8 %"$t" = alloca %"int64_(string[13],int64)__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$t") call void @llvm.memset.p0.i64(ptr align 1 %"$t", i8 0, i64 32, i1 false) %tuple1 = alloca %"int64_(string[3],int64)__tuple_t", align 8 %tuple = alloca %"string[3]_int64__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 16, i1 false) %1 = getelementptr %"string[3]_int64__tuple_t", ptr %tuple, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %1, ptr align 1 @hi, i64 3, i1 false) %2 = getelementptr %"string[3]_int64__tuple_t", ptr %tuple, i32 0, i32 1 store i64 3, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple1) call void @llvm.memset.p0.i64(ptr align 1 %tuple1, i8 0, i64 24, i1 false) %3 = getelementptr %"int64_(string[3],int64)__tuple_t", ptr %tuple1, i32 0, i32 0 store i64 1, ptr %3, align 8 %4 = getelementptr %"int64_(string[3],int64)__tuple_t", ptr %tuple1, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %4, ptr align 1 %tuple, i64 16, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %"$t", i8 0, i64 32, i1 false) %5 = getelementptr [24 x i8], ptr %tuple1, i64 0, i64 0 %6 = getelementptr %"int64_(string[13],int64)__tuple_t", ptr %"$t", i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %5, i64 8, i1 false) %7 = getelementptr [24 x i8], ptr %tuple1, i64 0, i64 8 %8 = getelementptr %"int64_(string[13],int64)__tuple_t", ptr %"$t", i32 0, i32 1 %9 = getelementptr [16 x i8], ptr %7, i64 0, i64 0 %10 = getelementptr %"string[13]_int64__tuple_t", ptr %8, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %10, ptr align 1 %9, i64 3, i1 false) %11 = getelementptr [16 x i8], ptr %7, i64 0, i64 8 %12 = getelementptr %"string[13]_int64__tuple_t", ptr %8, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %12, ptr align 1 %11, i64 8, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple1) call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple2) call void @llvm.memset.p0.i64(ptr align 1 %tuple2, i8 0, i64 24, i1 false) %13 = getelementptr %"string[13]_int64__tuple_t", ptr %tuple2, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %13, ptr align 1 @hellolongstr, i64 13, i1 false) %14 = getelementptr %"string[13]_int64__tuple_t", ptr %tuple2, i32 0, i32 1 store i64 4, ptr %14, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple3) call void @llvm.memset.p0.i64(ptr align 1 %tuple3, i8 0, i64 32, i1 false) %15 = getelementptr %"int64_(string[13],int64)__tuple_t", ptr %tuple3, i32 0, i32 0 store i64 1, ptr %15, align 8 %16 = getelementptr %"int64_(string[13],int64)__tuple_t", ptr %tuple3, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %16, ptr align 1 %tuple2, i64 24, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple2) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$t", ptr align 1 %tuple3, i64 32, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple3) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/nested_while_loop.ll000066400000000000000000000174021477746507000231650ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @interval_s_1_1(ptr %0) section "s_interval_s_1_1" !dbg !45 { entry: %"@_newval" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 %"$j" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$j") store i64 0, ptr %"$j", align 8 %"$i" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$i") store i64 0, ptr %"$i", align 8 store i64 1, ptr %"$i", align 8 br label %while_cond while_cond: ; preds = %while_end3, %entry %1 = load i64, ptr %"$i", align 8 %2 = icmp sle i64 %1, 100 %true_cond = icmp ne i1 %2, false br i1 %true_cond, label %while_body, label %while_end, !llvm.loop !52 while_body: ; preds = %while_cond store i64 0, ptr %"$j", align 8 %3 = load i64, ptr %"$i", align 8 %4 = add i64 %3, 1 store i64 %4, ptr %"$i", align 8 br label %while_cond1 while_end: ; preds = %while_cond ret i64 0 while_cond1: ; preds = %lookup_merge, %while_body %5 = load i64, ptr %"$j", align 8 %6 = icmp sle i64 %5, 100 %true_cond4 = icmp ne i1 %6, false br i1 %true_cond4, label %while_body2, label %while_end3, !llvm.loop !52 while_body2: ; preds = %while_cond1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure while_end3: ; preds = %while_cond1 br label %while_cond lookup_success: ; preds = %while_body2 %7 = load i64, ptr %lookup_elem, align 8 store i64 %7, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %while_body2 store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %8 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_newval") %9 = add i64 %8, 1 store i64 %9, ptr %"@_newval", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_newval", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_newval") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") %10 = load i64, ptr %"$j", align 8 %11 = add i64 %10, 1 store i64 %11, ptr %"$j", align 8 br label %while_cond1 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "interval_s_1_1", linkageName: "interval_s_1_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) !52 = distinct !{!52, !53} !53 = !{!"llvm.loop.unroll.disable"} bpftrace-0.23.2/tests/codegen/llvm/optional_positional_parameter.ll000066400000000000000000000174651477746507000256210ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !40 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !52 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !54 @0 = global [1 x i8] zeroinitializer ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !61 { entry: %"@y_key" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 0, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %2, i32 1024, i64 ptrtoint (ptr @0 to i64)) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %2, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!58} !llvm.module.flags = !{!60} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!5, !11, !12, !20} !20 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !21, size: 64, offset: 192) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !23, size: 8192, elements: !24) !23 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !24 = !{!25} !25 = !DISubrange(count: 1024, lowerBound: 0) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !15} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !52 = !DIGlobalVariableExpression(var: !53, expr: !DIExpression()) !53 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !54 = !DIGlobalVariableExpression(var: !55, expr: !DIExpression()) !55 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !56, isLocal: false, isDefinition: true) !56 = !DICompositeType(tag: DW_TAG_array_type, baseType: !57, size: 8192, elements: !9) !57 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 8192, elements: !9) !58 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !59) !59 = !{!0, !16, !26, !40, !52, !54} !60 = !{i32 2, !"Debug Info Version", i32 3} !61 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !62, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !58, retainedNodes: !65) !62 = !DISubroutineType(types: !63) !63 = !{!14, !64} !64 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64) !65 = !{!66} !66 = !DILocalVariable(name: "ctx", arg: 1, scope: !61, file: !2, type: !64) bpftrace-0.23.2/tests/codegen/llvm/pointer_add_int.ll000066400000000000000000000106551477746507000226270ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"$v" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$v") store i64 0, ptr %"$v", align 8 store i64 1000, ptr %"$v", align 8 %1 = load i64, ptr %"$v", align 8 %2 = add i64 %1, 20 store i64 %2, ptr %"$v", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/pointer_if_condition.ll000066400000000000000000000111511477746507000236610ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"$v" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$v") store i64 0, ptr %"$v", align 8 store i64 1, ptr %"$v", align 8 %1 = load i64, ptr %"$v", align 8 %true_cond = icmp ne i64 %1, 0 br i1 %true_cond, label %if_body, label %if_end if_body: ; preds = %entry br label %if_end if_end: ; preds = %if_body, %entry ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/pointer_inc_map.ll000066400000000000000000000154101477746507000226250ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@_newval" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key1" = alloca i64, align 8 %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1000, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key1") store i64 0, ptr %"@_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_, ptr %"@_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_newval") %3 = add i64 %2, 2 store i64 %3, ptr %"@_newval", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key1", ptr %"@_newval", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_newval") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key1") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/pointer_inc_var.ll000066400000000000000000000106541477746507000226450ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"$v" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$v") store i64 0, ptr %"$v", align 8 store i64 1000, ptr %"$v", align 8 %1 = load i64, ptr %"$v", align 8 %2 = add i64 %1, 2 store i64 %2, ptr %"$v", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/pointer_logical_and.ll000066400000000000000000000125221477746507000234540ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"&&_result" = alloca i64, align 8 %"$v" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$v") store i64 0, ptr %"$v", align 8 store i64 1, ptr %"$v", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"&&_result") %1 = load i64, ptr %"$v", align 8 %lhs_true_cond = icmp ne i64 %1, 0 br i1 %lhs_true_cond, label %"&&_lhs_true", label %"&&_false" if_body: ; preds = %"&&_merge" br label %if_end if_end: ; preds = %if_body, %"&&_merge" ret i64 0 "&&_lhs_true": ; preds = %entry br i1 false, label %"&&_true", label %"&&_false" "&&_true": ; preds = %"&&_lhs_true" store i64 1, ptr %"&&_result", align 8 br label %"&&_merge" "&&_false": ; preds = %"&&_lhs_true", %entry store i64 0, ptr %"&&_result", align 8 br label %"&&_merge" "&&_merge": ; preds = %"&&_false", %"&&_true" %2 = load i64, ptr %"&&_result", align 8 %true_cond = icmp ne i64 %2, 0 br i1 %true_cond, label %if_body, label %if_end } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/pointer_logical_or.ll000066400000000000000000000125241477746507000233340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"||_result" = alloca i64, align 8 %"$v" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$v") store i64 0, ptr %"$v", align 8 store i64 1, ptr %"$v", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"||_result") %1 = load i64, ptr %"$v", align 8 %lhs_true_cond = icmp ne i64 %1, 0 br i1 %lhs_true_cond, label %"||_true", label %"||_lhs_false" if_body: ; preds = %"||_merge" br label %if_end if_end: ; preds = %if_body, %"||_merge" ret i64 0 "||_lhs_false": ; preds = %entry br i1 false, label %"||_true", label %"||_false" "||_false": ; preds = %"||_lhs_false" store i64 0, ptr %"||_result", align 8 br label %"||_merge" "||_true": ; preds = %"||_lhs_false", %entry store i64 1, ptr %"||_result", align 8 br label %"||_merge" "||_merge": ; preds = %"||_true", %"||_false" %2 = load i64, ptr %"||_result", align 8 %true_cond = icmp ne i64 %2, 0 br i1 %true_cond, label %if_body, label %if_end } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/pointer_tenary_expression.ll000066400000000000000000000116041477746507000250010ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 %"$v" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$v") store i64 0, ptr %"$v", align 8 store i64 1, ptr %"$v", align 8 %1 = load i64, ptr %"$v", align 8 %true_cond = icmp ne i64 %1, 0 br i1 %true_cond, label %left, label %right left: ; preds = %entry br label %done right: ; preds = %entry br label %done done: ; preds = %right, %left %result = phi i64 [ 1, %left ], [ 0, %right ] store i64 %result, ptr %"$x", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/pred_binop.ll000066400000000000000000000136131477746507000216030ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %3 = icmp eq i64 %2, 1234 %4 = zext i1 %3 to i64 %predcond = icmp eq i64 %4, 0 br i1 %predcond, label %pred_false, label %pred_true pred_false: ; preds = %entry ret i64 0 pred_true: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/probe_str_scratch_buf.ll000066400000000000000000000145431477746507000240270ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !33 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !46 @map_key_buf = dso_local externally_initialized global [1 x [1 x [8 x i8]]] zeroinitializer, section ".data.map_key_buf", !dbg !48 @"tracepoint:sched:sched_one" = global [27 x i8] c"tracepoint:sched:sched_one\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_sched_sched_one_1(ptr %0) section "s_tracepoint_sched_sched_one_1" !dbg !58 { entry: %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [8 x i8]]], ptr @map_key_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 store i64 0, ptr %2, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %2, ptr @"tracepoint:sched:sched_one", i64 0) ret i64 1 } attributes #0 = { nounwind } !llvm.dbg.cu = !{!55} !llvm.module.flags = !{!57} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 216, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 27, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !28} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !19) !28 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !29, size: 64, offset: 64) !29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64) !30 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !31) !31 = !{!32} !32 = !DISubrange(count: 262144, lowerBound: 0) !33 = !DIGlobalVariableExpression(var: !34, expr: !DIExpression()) !34 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !35 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !36) !36 = !{!37, !11, !42, !45} !37 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !38, size: 64) !38 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !39, size: 64) !39 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !40) !40 = !{!41} !41 = !DISubrange(count: 2, lowerBound: 0) !42 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !43, size: 64, offset: 128) !43 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !44, size: 64) !44 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !45 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !46 = !DIGlobalVariableExpression(var: !47, expr: !DIExpression()) !47 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !48 = !DIGlobalVariableExpression(var: !49, expr: !DIExpression()) !49 = distinct !DIGlobalVariable(name: "map_key_buf", linkageName: "global", scope: !2, file: !2, type: !50, isLocal: false, isDefinition: true) !50 = !DICompositeType(tag: DW_TAG_array_type, baseType: !51, size: 64, elements: !9) !51 = !DICompositeType(tag: DW_TAG_array_type, baseType: !52, size: 64, elements: !9) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 64, elements: !53) !53 = !{!54} !54 = !DISubrange(count: 8, lowerBound: 0) !55 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !56) !56 = !{!0, !21, !33, !46, !48} !57 = !{i32 2, !"Debug Info Version", i32 3} !58 = distinct !DISubprogram(name: "tracepoint_sched_sched_one_1", linkageName: "tracepoint_sched_sched_one_1", scope: !2, file: !2, type: !59, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !55, retainedNodes: !62) !59 = !DISubroutineType(types: !60) !60 = !{!14, !61} !61 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !62 = !{!63} !63 = !DILocalVariable(name: "ctx", arg: 1, scope: !58, file: !2, type: !61) bpftrace-0.23.2/tests/codegen/llvm/probe_str_stack.ll000066400000000000000000000133371477746507000226510ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !33 @"tracepoint:sched:sched_one" = global [27 x i8] c"tracepoint:sched:sched_one\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_sched_sched_one_1(ptr %0) section "s_tracepoint_sched_sched_one_1" !dbg !49 { entry: %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr @"tracepoint:sched:sched_one", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 1 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 216, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 27, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !28} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !19) !28 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !29, size: 64, offset: 64) !29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64) !30 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !31) !31 = !{!32} !32 = !DISubrange(count: 262144, lowerBound: 0) !33 = !DIGlobalVariableExpression(var: !34, expr: !DIExpression()) !34 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !35 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !36) !36 = !{!37, !11, !42, !45} !37 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !38, size: 64) !38 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !39, size: 64) !39 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !40) !40 = !{!41} !41 = !DISubrange(count: 2, lowerBound: 0) !42 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !43, size: 64, offset: 128) !43 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !44, size: 64) !44 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !45 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !21, !33} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "tracepoint_sched_sched_one_1", linkageName: "tracepoint_sched_sched_one_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!14, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/ptr_to_ptr.ll000066400000000000000000000123151477746507000216540ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"$res" = alloca i32, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i32 0, ptr %"$res", align 4 %deref1 = alloca i32, align 4 %deref = alloca i64, align 8 %"$pp" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$pp") store i64 0, ptr %"$pp", align 8 store i64 0, ptr %"$pp", align 8 %1 = load i64, ptr %"$pp", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %deref) %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %deref, i32 8, i64 %1) %2 = load i64, ptr %deref, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %deref) call void @llvm.lifetime.start.p0(i64 -1, ptr %deref1) %probe_read_kernel2 = call i64 inttoptr (i64 113 to ptr)(ptr %deref1, i32 4, i64 %2) %3 = load i32, ptr %deref1, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %deref1) store i32 %3, ptr %"$res", align 4 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_comm.ll000066400000000000000000000240571477746507000245320ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %key8 = alloca i32, align 4 %helper_error_t3 = alloca %helper_error_t, align 8 %"@x_key" = alloca i64, align 8 %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %comm = alloca [16 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %comm) call void @llvm.memset.p0.i64(ptr align 1 %comm, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to ptr)(ptr %comm, i64 16) %1 = trunc i64 %get_comm to i32 %2 = icmp sge i32 %1, 0 br i1 %2, label %helper_merge, label %helper_failure helper_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %3 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %3, align 8 %4 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %4, align 8 %5 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %1, ptr %5, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %comm, i64 0) %6 = trunc i64 %update_elem to i32 %7 = icmp sge i32 %6, 0 br i1 %7, label %helper_merge2, label %helper_failure1 event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success: ; preds = %event_loss_counter %8 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge helper_failure1: ; preds = %helper_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t3) %9 = getelementptr %helper_error_t, ptr %helper_error_t3, i64 0, i32 0 store i64 30006, ptr %9, align 8 %10 = getelementptr %helper_error_t, ptr %helper_error_t3, i64 0, i32 1 store i64 1, ptr %10, align 8 %11 = getelementptr %helper_error_t, ptr %helper_error_t3, i64 0, i32 2 store i32 %6, ptr %11, align 4 %ringbuf_output4 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t3, i64 20, i64 0) %ringbuf_loss7 = icmp slt i64 %ringbuf_output4, 0 br i1 %ringbuf_loss7, label %event_loss_counter5, label %counter_merge6 helper_merge2: ; preds = %counter_merge6, %helper_merge call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %comm) ret i64 0 event_loss_counter5: ; preds = %helper_failure1 call void @llvm.lifetime.start.p0(i64 -1, ptr %key8) store i32 0, ptr %key8, align 4 %lookup_elem9 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key8) %map_lookup_cond13 = icmp ne ptr %lookup_elem9, null br i1 %map_lookup_cond13, label %lookup_success10, label %lookup_failure11 counter_merge6: ; preds = %lookup_merge12, %helper_failure1 call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t3) br label %helper_merge2 lookup_success10: ; preds = %event_loss_counter5 %12 = atomicrmw add ptr %lookup_elem9, i64 1 seq_cst, align 8 br label %lookup_merge12 lookup_failure11: ; preds = %event_loss_counter5 br label %lookup_merge12 lookup_merge12: ; preds = %lookup_failure11, %lookup_success10 call void @llvm.lifetime.end.p0(i64 -1, ptr %key8) br label %counter_merge6 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 16, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_delete.ll000066400000000000000000000237611477746507000250420ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %key9 = alloca i32, align 4 %helper_error_t4 = alloca %helper_error_t, align 8 %"@x_key1" = alloca i64, align 8 %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) %1 = trunc i64 %update_elem to i32 %2 = icmp sge i32 %1, 0 br i1 %2, label %helper_merge, label %helper_failure helper_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %3 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %3, align 8 %4 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %4, align 8 %5 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %1, ptr %5, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 1, ptr %"@x_key1", align 8 %delete_elem = call i64 inttoptr (i64 3 to ptr)(ptr @AT_x, ptr %"@x_key1") %6 = trunc i64 %delete_elem to i32 %7 = icmp sge i32 %6, 0 br i1 %7, label %helper_merge3, label %helper_failure2 event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success: ; preds = %event_loss_counter %8 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge helper_failure2: ; preds = %helper_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t4) %9 = getelementptr %helper_error_t, ptr %helper_error_t4, i64 0, i32 0 store i64 30006, ptr %9, align 8 %10 = getelementptr %helper_error_t, ptr %helper_error_t4, i64 0, i32 1 store i64 1, ptr %10, align 8 %11 = getelementptr %helper_error_t, ptr %helper_error_t4, i64 0, i32 2 store i32 %6, ptr %11, align 4 %ringbuf_output5 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t4, i64 20, i64 0) %ringbuf_loss8 = icmp slt i64 %ringbuf_output5, 0 br i1 %ringbuf_loss8, label %event_loss_counter6, label %counter_merge7 helper_merge3: ; preds = %counter_merge7, %helper_merge call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") ret i64 0 event_loss_counter6: ; preds = %helper_failure2 call void @llvm.lifetime.start.p0(i64 -1, ptr %key9) store i32 0, ptr %key9, align 4 %lookup_elem10 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key9) %map_lookup_cond14 = icmp ne ptr %lookup_elem10, null br i1 %map_lookup_cond14, label %lookup_success11, label %lookup_failure12 counter_merge7: ; preds = %lookup_merge13, %helper_failure2 call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t4) br label %helper_merge3 lookup_success11: ; preds = %event_loss_counter6 %12 = atomicrmw add ptr %lookup_elem10, i64 1 seq_cst, align 8 br label %lookup_merge13 lookup_failure12: ; preds = %event_loss_counter6 br label %lookup_merge13 lookup_merge13: ; preds = %lookup_failure12, %lookup_success11 call void @llvm.lifetime.end.p0(i64 -1, ptr %key9) br label %counter_merge7 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_for_map.ll000066400000000000000000000346701477746507000252240ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> %int64_int64__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_map = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_x = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !31 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !45 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !60 { entry: %key8 = alloca i32, align 4 %helper_error_t3 = alloca %helper_error_t, align 8 %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %"@map_val" = alloca i64, align 8 %"@map_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_key") store i64 16, ptr %"@map_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_val") store i64 32, ptr %"@map_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_map, ptr %"@map_key", ptr %"@map_val", i64 0) %1 = trunc i64 %update_elem to i32 %2 = icmp sge i32 %1, 0 br i1 %2, label %helper_merge, label %helper_failure helper_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %3 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %3, align 8 %4 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %4, align 8 %5 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %1, ptr %5, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_key") %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_map, ptr @map_for_each_cb, ptr null, i64 0) %6 = trunc i64 %for_each_map_elem to i32 %7 = icmp sge i32 %6, 0 br i1 %7, label %helper_merge2, label %helper_failure1 event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success: ; preds = %event_loss_counter %8 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge helper_failure1: ; preds = %helper_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t3) %9 = getelementptr %helper_error_t, ptr %helper_error_t3, i64 0, i32 0 store i64 30006, ptr %9, align 8 %10 = getelementptr %helper_error_t, ptr %helper_error_t3, i64 0, i32 1 store i64 2, ptr %10, align 8 %11 = getelementptr %helper_error_t, ptr %helper_error_t3, i64 0, i32 2 store i32 %6, ptr %11, align 4 %ringbuf_output4 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t3, i64 20, i64 0) %ringbuf_loss7 = icmp slt i64 %ringbuf_output4, 0 br i1 %ringbuf_loss7, label %event_loss_counter5, label %counter_merge6 helper_merge2: ; preds = %counter_merge6, %helper_merge ret i64 0 event_loss_counter5: ; preds = %helper_failure1 call void @llvm.lifetime.start.p0(i64 -1, ptr %key8) store i32 0, ptr %key8, align 4 %lookup_elem9 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key8) %map_lookup_cond13 = icmp ne ptr %lookup_elem9, null br i1 %map_lookup_cond13, label %lookup_success10, label %lookup_failure11 counter_merge6: ; preds = %lookup_merge12, %helper_failure1 call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t3) br label %helper_merge2 lookup_success10: ; preds = %event_loss_counter5 %12 = atomicrmw add ptr %lookup_elem9, i64 1 seq_cst, align 8 br label %lookup_merge12 lookup_failure11: ; preds = %event_loss_counter5 br label %lookup_merge12 lookup_merge12: ; preds = %lookup_failure11, %lookup_success10 call void @llvm.lifetime.end.p0(i64 -1, ptr %key8) br label %counter_merge6 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !67 { %key1 = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %"@x_key" = alloca i64, align 8 %"$kv" = alloca %int64_int64__tuple_t, align 8 %key = load i64, ptr %1, align 8 %val = load i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %5 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %5, align 8 %6 = getelementptr %int64_int64__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %val, ptr %6, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"$kv", i64 0) %7 = trunc i64 %update_elem to i32 %8 = icmp sge i32 %7, 0 br i1 %8, label %helper_merge, label %helper_failure helper_failure: ; preds = %4 call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %9 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %9, align 8 %10 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 1, ptr %10, align 8 %11 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %7, ptr %11, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key1) store i32 0, ptr %key1, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key1) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success: ; preds = %event_loss_counter %12 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key1) br label %counter_merge } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!57} !llvm.module.flags = !{!59} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_map", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !23) !23 = !{!5, !24, !16, !25} !24 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !25 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !26, size: 64, offset: 192) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !30} !29 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64) !30 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !18, size: 64, offset: 64) !31 = !DIGlobalVariableExpression(var: !32, expr: !DIExpression()) !32 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !33, isLocal: false, isDefinition: true) !33 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !34) !34 = !{!35, !40} !35 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !36, size: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 27, lowerBound: 0) !40 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !41, size: 64, offset: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 262144, lowerBound: 0) !45 = !DIGlobalVariableExpression(var: !46, expr: !DIExpression()) !46 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !47, isLocal: false, isDefinition: true) !47 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !48) !48 = !{!49, !24, !54, !19} !49 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !50, size: 64) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !52) !52 = !{!53} !53 = !DISubrange(count: 2, lowerBound: 0) !54 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !55, size: 64, offset: 128) !55 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !56, size: 64) !56 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !57 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !58) !58 = !{!0, !20, !31, !45} !59 = !{i32 2, !"Debug Info Version", i32 3} !60 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !57, retainedNodes: !65) !61 = !DISubroutineType(types: !62) !62 = !{!18, !63} !63 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !64, size: 64) !64 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !65 = !{!66} !66 = !DILocalVariable(name: "ctx", arg: 1, scope: !60, file: !2, type: !63) !67 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !57, retainedNodes: !70) !68 = !DISubroutineType(types: !69) !69 = !{!18, !63, !63, !63, !63} !70 = !{!71, !72, !73, !74} !71 = !DILocalVariable(name: "map", arg: 1, scope: !67, file: !2, type: !63) !72 = !DILocalVariable(name: "key", arg: 2, scope: !67, file: !2, type: !63) !73 = !DILocalVariable(name: "value", arg: 3, scope: !67, file: !2, type: !63) !74 = !DILocalVariable(name: "ctx", arg: 4, scope: !67, file: !2, type: !63) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_lookup.ll000066400000000000000000000237131477746507000251060ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %key11 = alloca i32, align 4 %helper_error_t6 = alloca %helper_error_t, align 8 %"@_newval" = alloca i64, align 8 %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %2 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %2, align 8 %3 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %3, align 8 %4 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 0, ptr %4, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge lookup_merge: ; preds = %counter_merge, %lookup_success %5 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_newval") %6 = add i64 %5, 1 store i64 %6, ptr %"@_newval", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_newval", i64 0) %7 = trunc i64 %update_elem to i32 %8 = icmp sge i32 %7, 0 br i1 %8, label %helper_merge, label %helper_failure event_loss_counter: ; preds = %lookup_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem1 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond5 = icmp ne ptr %lookup_elem1, null br i1 %map_lookup_cond5, label %lookup_success2, label %lookup_failure3 counter_merge: ; preds = %lookup_merge4, %lookup_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %lookup_merge lookup_success2: ; preds = %event_loss_counter %9 = atomicrmw add ptr %lookup_elem1, i64 1 seq_cst, align 8 br label %lookup_merge4 lookup_failure3: ; preds = %event_loss_counter br label %lookup_merge4 lookup_merge4: ; preds = %lookup_failure3, %lookup_success2 call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge helper_failure: ; preds = %lookup_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t6) %10 = getelementptr %helper_error_t, ptr %helper_error_t6, i64 0, i32 0 store i64 30006, ptr %10, align 8 %11 = getelementptr %helper_error_t, ptr %helper_error_t6, i64 0, i32 1 store i64 1, ptr %11, align 8 %12 = getelementptr %helper_error_t, ptr %helper_error_t6, i64 0, i32 2 store i32 %7, ptr %12, align 4 %ringbuf_output7 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t6, i64 20, i64 0) %ringbuf_loss10 = icmp slt i64 %ringbuf_output7, 0 br i1 %ringbuf_loss10, label %event_loss_counter8, label %counter_merge9 helper_merge: ; preds = %counter_merge9, %lookup_merge call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_newval") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 event_loss_counter8: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key11) store i32 0, ptr %key11, align 4 %lookup_elem12 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key11) %map_lookup_cond16 = icmp ne ptr %lookup_elem12, null br i1 %map_lookup_cond16, label %lookup_success13, label %lookup_failure14 counter_merge9: ; preds = %lookup_merge15, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t6) br label %helper_merge lookup_success13: ; preds = %event_loss_counter8 %13 = atomicrmw add ptr %lookup_elem12, i64 1 seq_cst, align 8 br label %lookup_merge15 lookup_failure14: ; preds = %event_loss_counter8 br label %lookup_merge15 lookup_merge15: ; preds = %lookup_failure14, %lookup_success13 call void @llvm.lifetime.end.p0(i64 -1, ptr %key11) br label %counter_merge9 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_lookup_no_warning.ll000066400000000000000000000204261477746507000273250ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %"@_newval" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_newval") %3 = add i64 %2, 1 store i64 %3, ptr %"@_newval", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_newval", i64 0) %4 = trunc i64 %update_elem to i32 %5 = icmp sge i32 %4, 0 br i1 %5, label %helper_merge, label %helper_failure helper_failure: ; preds = %lookup_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %6 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %6, align 8 %7 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %7, align 8 %8 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %4, ptr %8, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %lookup_merge call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_newval") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem1 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond5 = icmp ne ptr %lookup_elem1, null br i1 %map_lookup_cond5, label %lookup_success2, label %lookup_failure3 counter_merge: ; preds = %lookup_merge4, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success2: ; preds = %event_loss_counter %9 = atomicrmw add ptr %lookup_elem1, i64 1 seq_cst, align 8 br label %lookup_merge4 lookup_failure3: ; preds = %event_loss_counter br label %lookup_merge4 lookup_merge4: ; preds = %lookup_failure3, %lookup_success2 call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_lookup_percpu.ll000066400000000000000000000312261477746507000264620ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !22 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !36 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !45 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %"$a" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$a") store i64 0, ptr %"$a", align 8 %key15 = alloca i32, align 4 %helper_error_t10 = alloca %helper_error_t, align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %"@_key6" = alloca i64, align 8 %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 %2 = add i64 %1, 1 store i64 %2, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 1, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %initial_value, i64 1) %3 = trunc i64 %update_elem to i32 %4 = icmp sge i32 %3, 0 br i1 %4, label %helper_merge, label %helper_failure lookup_merge: ; preds = %helper_merge, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key6") store i64 0, ptr %"@_key6", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond helper_failure: ; preds = %lookup_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %5 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %5, align 8 %6 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %6, align 8 %7 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %3, ptr %7, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %lookup_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem1 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond5 = icmp ne ptr %lookup_elem1, null br i1 %map_lookup_cond5, label %lookup_success2, label %lookup_failure3 counter_merge: ; preds = %lookup_merge4, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success2: ; preds = %event_loss_counter %8 = atomicrmw add ptr %lookup_elem1, i64 1 seq_cst, align 8 br label %lookup_merge4 lookup_failure3: ; preds = %event_loss_counter br label %lookup_merge4 lookup_merge4: ; preds = %lookup_failure3, %lookup_success2 call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge while_cond: ; preds = %lookup_success7, %lookup_merge %9 = load i32, ptr @num_cpus, align 4 %10 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %10, %9 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %11 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_, ptr %"@_key6", i32 %11) %map_lookup_cond9 = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond9, label %lookup_success7, label %lookup_failure8 while_end: ; preds = %error_failure, %counter_merge13, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %12 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key6") store i64 %12, ptr %"$a", align 8 ret i64 0 lookup_success7: ; preds = %while_body %13 = load i64, ptr %val_1, align 8 %14 = load i64, ptr %lookup_percpu_elem, align 8 %15 = add i64 %14, %13 store i64 %15, ptr %val_1, align 8 %16 = load i32, ptr %i, align 4 %17 = add i32 %16, 1 store i32 %17, ptr %i, align 4 br label %while_cond lookup_failure8: ; preds = %while_body %18 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %18, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure error_success: ; preds = %lookup_failure8 call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t10) %19 = getelementptr %helper_error_t, ptr %helper_error_t10, i64 0, i32 0 store i64 30006, ptr %19, align 8 %20 = getelementptr %helper_error_t, ptr %helper_error_t10, i64 0, i32 1 store i64 1, ptr %20, align 8 %21 = getelementptr %helper_error_t, ptr %helper_error_t10, i64 0, i32 2 store i32 0, ptr %21, align 4 %ringbuf_output11 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t10, i64 20, i64 0) %ringbuf_loss14 = icmp slt i64 %ringbuf_output11, 0 br i1 %ringbuf_loss14, label %event_loss_counter12, label %counter_merge13 error_failure: ; preds = %lookup_failure8 %22 = load i32, ptr %i, align 4 br label %while_end event_loss_counter12: ; preds = %error_success call void @llvm.lifetime.start.p0(i64 -1, ptr %key15) store i32 0, ptr %key15, align 4 %lookup_elem16 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key15) %map_lookup_cond20 = icmp ne ptr %lookup_elem16, null br i1 %map_lookup_cond20, label %lookup_success17, label %lookup_failure18 counter_merge13: ; preds = %lookup_merge19, %error_success call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t10) br label %while_end lookup_success17: ; preds = %event_loss_counter12 %23 = atomicrmw add ptr %lookup_elem16, i64 1 seq_cst, align 8 br label %lookup_merge19 lookup_failure18: ; preds = %event_loss_counter12 br label %lookup_merge19 lookup_merge19: ; preds = %lookup_failure18, %lookup_success17 call void @llvm.lifetime.end.p0(i64 -1, ptr %key15) br label %counter_merge13 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 6, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !20, size: 64, offset: 192) !20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !21 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !22 = !DIGlobalVariableExpression(var: !23, expr: !DIExpression()) !23 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !24, isLocal: false, isDefinition: true) !24 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !25) !25 = !{!26, !31} !26 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !27, size: 64) !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !28, size: 64) !28 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !29) !29 = !{!30} !30 = !DISubrange(count: 27, lowerBound: 0) !31 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !32, size: 64, offset: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64) !33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !34) !34 = !{!35} !35 = !DISubrange(count: 262144, lowerBound: 0) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !38, isLocal: false, isDefinition: true) !38 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !39) !39 = !{!40, !11, !16, !19} !40 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !41, size: 64) !41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !42, size: 64) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !43) !43 = !{!44} !44 = !DISubrange(count: 2, lowerBound: 0) !45 = !DIGlobalVariableExpression(var: !46, expr: !DIExpression()) !46 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !21, isLocal: false, isDefinition: true) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !22, !36, !45} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!21, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_path.ll000066400000000000000000000173561477746507000245370ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !38 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @fentry_mock_vmlinux_filp_close_1(ptr %0) section "s_fentry_mock_vmlinux_filp_close_1" !dbg !49 { entry: %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %d_path = call i64 inttoptr (i64 147 to ptr)(ptr null, ptr %2, i32 1024) %3 = trunc i64 %d_path to i32 %4 = icmp sge i32 %3, 0 br i1 %4, label %helper_merge, label %helper_failure helper_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %5 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %5, align 8 %6 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %6, align 8 %7 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %3, ptr %7, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %entry ret i64 0 event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success: ; preds = %event_loss_counter %8 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 8192, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 8192, elements: !28) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 8192, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 1024, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "fentry_mock_vmlinux_filp_close_1", linkageName: "fentry_mock_vmlinux_filp_close_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_pid_tid.ll000066400000000000000000000247331477746507000252140ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !18 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !32 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !47 { entry: %key10 = alloca i32, align 4 %helper_error_t5 = alloca %helper_error_t, align 8 %"@y_val" = alloca i64, align 8 %"@y_key" = alloca i64, align 8 %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %2 = zext i32 %pid to i64 store i64 %2, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) %3 = trunc i64 %update_elem to i32 %4 = icmp sge i32 %3, 0 br i1 %4, label %helper_merge, label %helper_failure helper_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %5 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %5, align 8 %6 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %6, align 8 %7 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %3, ptr %7, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %entry call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %get_pid_tgid1 = call i64 inttoptr (i64 14 to ptr)() %tid = trunc i64 %get_pid_tgid1 to i32 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_val") %8 = zext i32 %tid to i64 store i64 %8, ptr %"@y_val", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"@y_val", i64 0) %9 = trunc i64 %update_elem2 to i32 %10 = icmp sge i32 %9, 0 br i1 %10, label %helper_merge4, label %helper_failure3 event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success: ; preds = %event_loss_counter %11 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge helper_failure3: ; preds = %helper_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t5) %12 = getelementptr %helper_error_t, ptr %helper_error_t5, i64 0, i32 0 store i64 30006, ptr %12, align 8 %13 = getelementptr %helper_error_t, ptr %helper_error_t5, i64 0, i32 1 store i64 1, ptr %13, align 8 %14 = getelementptr %helper_error_t, ptr %helper_error_t5, i64 0, i32 2 store i32 %9, ptr %14, align 4 %ringbuf_output6 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t5, i64 20, i64 0) %ringbuf_loss9 = icmp slt i64 %ringbuf_output6, 0 br i1 %ringbuf_loss9, label %event_loss_counter7, label %counter_merge8 helper_merge4: ; preds = %counter_merge8, %helper_merge call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 event_loss_counter7: ; preds = %helper_failure3 call void @llvm.lifetime.start.p0(i64 -1, ptr %key10) store i32 0, ptr %key10, align 4 %lookup_elem11 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key10) %map_lookup_cond15 = icmp ne ptr %lookup_elem11, null br i1 %map_lookup_cond15, label %lookup_success12, label %lookup_failure13 counter_merge8: ; preds = %lookup_merge14, %helper_failure3 call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t5) br label %helper_merge4 lookup_success12: ; preds = %event_loss_counter7 %15 = atomicrmw add ptr %lookup_elem11, i64 1 seq_cst, align 8 br label %lookup_merge14 lookup_failure13: ; preds = %event_loss_counter7 br label %lookup_merge14 lookup_merge14: ; preds = %lookup_failure13, %lookup_success12 call void @llvm.lifetime.end.p0(i64 -1, ptr %key10) br label %counter_merge8 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!44} !llvm.module.flags = !{!46} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) !19 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !20, isLocal: false, isDefinition: true) !20 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !21) !21 = !{!22, !27} !22 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !23, size: 64) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !25) !25 = !{!26} !26 = !DISubrange(count: 27, lowerBound: 0) !27 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !28, size: 64, offset: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 262144, lowerBound: 0) !32 = !DIGlobalVariableExpression(var: !33, expr: !DIExpression()) !33 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !34, isLocal: false, isDefinition: true) !34 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !35) !35 = !{!36, !11, !41, !15} !36 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !39) !39 = !{!40} !40 = !DISubrange(count: 2, lowerBound: 0) !41 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !42, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !44 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !45) !45 = !{!0, !16, !18, !32} !46 = !{i32 2, !"Debug Info Version", i32 3} !47 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !44, retainedNodes: !52) !48 = !DISubroutineType(types: !49) !49 = !{!14, !50} !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !52 = !{!53} !53 = !DILocalVariable(name: "ctx", arg: 1, scope: !47, file: !2, type: !50) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_printf.ll000066400000000000000000000166301477746507000250770ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @__fmt_0 = internal constant [3 x i8] c"%d\00", align 1 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @iter_task_file_1(ptr %0) section "s_iter_task_file_1" !dbg !39 { entry: %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %data = alloca [1 x i64], align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %data) call void @llvm.memset.p0.i64(ptr align 1 %data, i8 0, i64 8, i1 false) %1 = getelementptr [1 x i64], ptr %data, i64 0, i32 0 store i64 1, ptr %1, align 8 %2 = call ptr @llvm.preserve.static.offset(ptr %0) %3 = getelementptr i64, ptr %2, i64 0 %meta = load volatile ptr, ptr %3, align 8 %4 = getelementptr i64, ptr %meta, i64 0 %seq = load i64, ptr %4, align 8 %seq_printf = call i64 inttoptr (i64 126 to ptr)(i64 %seq, ptr @__fmt_0, i32 3, ptr %data, i32 8) %5 = trunc i64 %seq_printf to i32 %6 = icmp sge i32 %5, 0 br i1 %6, label %helper_merge, label %helper_failure helper_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %7 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %7, align 8 %8 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %8, align 8 %9 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %5, ptr %9, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %entry ret i64 0 event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success: ; preds = %event_loss_counter %10 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "iter_task_file_1", linkageName: "iter_task_file_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_signal.ll000066400000000000000000000145231477746507000250510ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %signal = call i64 inttoptr (i64 109 to ptr)(i32 8) %1 = trunc i64 %signal to i32 %2 = icmp sge i32 %1, 0 br i1 %2, label %helper_merge, label %helper_failure helper_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %3 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %3, align 8 %4 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %4, align 8 %5 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %1, ptr %5, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %entry ret i64 0 event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success: ; preds = %event_loss_counter %6 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/runtime_error_check_stack.ll000066400000000000000000000717461477746507000247130ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } %"struct map_t.3" = type { ptr, ptr } %"struct map_t.4" = type { ptr, ptr, ptr, ptr } %helper_error_t = type <{ i64, i64, i32 }> %kstack_key = type { i64, i64 } %ustack_key = type { i64, i64, i32, i32 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @stack_bpftrace_127 = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 @stack_scratch = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !54 @ringbuf = dso_local global %"struct map_t.3" zeroinitializer, section ".maps", !dbg !66 @event_loss_counter = dso_local global %"struct map_t.4" zeroinitializer, section ".maps", !dbg !80 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !93 { entry: %key76 = alloca i32, align 4 %helper_error_t71 = alloca %helper_error_t, align 8 %"@y_key" = alloca i64, align 8 %key62 = alloca i32, align 4 %helper_error_t57 = alloca %helper_error_t, align 8 %key47 = alloca i32, align 4 %helper_error_t42 = alloca %helper_error_t, align 8 %lookup_stack_scratch_key31 = alloca i32, align 4 %stack_key28 = alloca %kstack_key, align 8 %key22 = alloca i32, align 4 %helper_error_t17 = alloca %helper_error_t, align 8 %"@x_key" = alloca i64, align 8 %key8 = alloca i32, align 4 %helper_error_t3 = alloca %helper_error_t, align 8 %key = alloca i32, align 4 %helper_error_t = alloca %helper_error_t, align 8 %lookup_stack_scratch_key = alloca i32, align 4 %stack_key = alloca %ustack_key, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key) call void @llvm.memset.p0.i64(ptr align 1 %stack_key, i8 0, i64 24, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key) store i32 0, ptr %lookup_stack_scratch_key, align 4 %lookup_stack_scratch_map = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key) %lookup_stack_scratch_cond = icmp ne ptr %lookup_stack_scratch_map, null br i1 %lookup_stack_scratch_cond, label %lookup_stack_scratch_merge, label %lookup_stack_scratch_failure stack_scratch_failure: ; preds = %lookup_stack_scratch_failure br label %merge_block merge_block: ; preds = %stack_scratch_failure, %helper_merge2, %get_stack_fail %1 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 2 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %2 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %2 to i32 store i32 %pid, ptr %1, align 4 %3 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 3 store i32 0, ptr %3, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem14 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %stack_key, i64 0) %4 = trunc i64 %update_elem14 to i32 %5 = icmp sge i32 %4, 0 br i1 %5, label %helper_merge16, label %helper_failure15 lookup_stack_scratch_failure: ; preds = %entry br label %stack_scratch_failure lookup_stack_scratch_merge: ; preds = %entry %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map, i32 1016, ptr null) %get_stack = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map, i32 1016, i64 256) %6 = trunc i64 %get_stack to i32 %7 = icmp sge i32 %6, 0 br i1 %7, label %helper_merge, label %helper_failure get_stack_success: ; preds = %helper_merge %8 = udiv i64 %get_stack, 8 %9 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 1 store i64 %8, ptr %9, align 8 %10 = trunc i64 %8 to i8 %murmur_hash_2 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map, i8 %10, i64 1) %11 = getelementptr %ustack_key, ptr %stack_key, i64 0, i32 0 store i64 %murmur_hash_2, ptr %11, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_127, ptr %stack_key, ptr %lookup_stack_scratch_map, i64 0) %12 = trunc i64 %update_elem to i32 %13 = icmp sge i32 %12, 0 br i1 %13, label %helper_merge2, label %helper_failure1 get_stack_fail: ; preds = %helper_merge br label %merge_block helper_failure: ; preds = %lookup_stack_scratch_merge call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t) %14 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 0 store i64 30006, ptr %14, align 8 %15 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 1 store i64 0, ptr %15, align 8 %16 = getelementptr %helper_error_t, ptr %helper_error_t, i64 0, i32 2 store i32 %6, ptr %16, align 4 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t, i64 20, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge helper_merge: ; preds = %counter_merge, %lookup_stack_scratch_merge %17 = icmp sge i64 %get_stack, 0 br i1 %17, label %get_stack_success, label %get_stack_fail event_loss_counter: ; preds = %helper_failure call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %helper_failure call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t) br label %helper_merge lookup_success: ; preds = %event_loss_counter %18 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge helper_failure1: ; preds = %get_stack_success call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t3) %19 = getelementptr %helper_error_t, ptr %helper_error_t3, i64 0, i32 0 store i64 30006, ptr %19, align 8 %20 = getelementptr %helper_error_t, ptr %helper_error_t3, i64 0, i32 1 store i64 1, ptr %20, align 8 %21 = getelementptr %helper_error_t, ptr %helper_error_t3, i64 0, i32 2 store i32 %12, ptr %21, align 4 %ringbuf_output4 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t3, i64 20, i64 0) %ringbuf_loss7 = icmp slt i64 %ringbuf_output4, 0 br i1 %ringbuf_loss7, label %event_loss_counter5, label %counter_merge6 helper_merge2: ; preds = %counter_merge6, %get_stack_success br label %merge_block event_loss_counter5: ; preds = %helper_failure1 call void @llvm.lifetime.start.p0(i64 -1, ptr %key8) store i32 0, ptr %key8, align 4 %lookup_elem9 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key8) %map_lookup_cond13 = icmp ne ptr %lookup_elem9, null br i1 %map_lookup_cond13, label %lookup_success10, label %lookup_failure11 counter_merge6: ; preds = %lookup_merge12, %helper_failure1 call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t3) br label %helper_merge2 lookup_success10: ; preds = %event_loss_counter5 %22 = atomicrmw add ptr %lookup_elem9, i64 1 seq_cst, align 8 br label %lookup_merge12 lookup_failure11: ; preds = %event_loss_counter5 br label %lookup_merge12 lookup_merge12: ; preds = %lookup_failure11, %lookup_success10 call void @llvm.lifetime.end.p0(i64 -1, ptr %key8) br label %counter_merge6 helper_failure15: ; preds = %merge_block call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t17) %23 = getelementptr %helper_error_t, ptr %helper_error_t17, i64 0, i32 0 store i64 30006, ptr %23, align 8 %24 = getelementptr %helper_error_t, ptr %helper_error_t17, i64 0, i32 1 store i64 2, ptr %24, align 8 %25 = getelementptr %helper_error_t, ptr %helper_error_t17, i64 0, i32 2 store i32 %4, ptr %25, align 4 %ringbuf_output18 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t17, i64 20, i64 0) %ringbuf_loss21 = icmp slt i64 %ringbuf_output18, 0 br i1 %ringbuf_loss21, label %event_loss_counter19, label %counter_merge20 helper_merge16: ; preds = %counter_merge20, %merge_block call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %stack_key28) call void @llvm.memset.p0.i64(ptr align 1 %stack_key28, i8 0, i64 16, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_stack_scratch_key31) store i32 0, ptr %lookup_stack_scratch_key31, align 4 %lookup_stack_scratch_map32 = call ptr inttoptr (i64 1 to ptr)(ptr @stack_scratch, ptr %lookup_stack_scratch_key31) call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_stack_scratch_key31) %lookup_stack_scratch_cond35 = icmp ne ptr %lookup_stack_scratch_map32, null br i1 %lookup_stack_scratch_cond35, label %lookup_stack_scratch_merge34, label %lookup_stack_scratch_failure33 event_loss_counter19: ; preds = %helper_failure15 call void @llvm.lifetime.start.p0(i64 -1, ptr %key22) store i32 0, ptr %key22, align 4 %lookup_elem23 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key22) %map_lookup_cond27 = icmp ne ptr %lookup_elem23, null br i1 %map_lookup_cond27, label %lookup_success24, label %lookup_failure25 counter_merge20: ; preds = %lookup_merge26, %helper_failure15 call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t17) br label %helper_merge16 lookup_success24: ; preds = %event_loss_counter19 %26 = atomicrmw add ptr %lookup_elem23, i64 1 seq_cst, align 8 br label %lookup_merge26 lookup_failure25: ; preds = %event_loss_counter19 br label %lookup_merge26 lookup_merge26: ; preds = %lookup_failure25, %lookup_success24 call void @llvm.lifetime.end.p0(i64 -1, ptr %key22) br label %counter_merge20 stack_scratch_failure29: ; preds = %lookup_stack_scratch_failure33 br label %merge_block30 merge_block30: ; preds = %stack_scratch_failure29, %helper_merge56, %get_stack_fail38 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 %update_elem68 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %stack_key28, i64 0) %27 = trunc i64 %update_elem68 to i32 %28 = icmp sge i32 %27, 0 br i1 %28, label %helper_merge70, label %helper_failure69 lookup_stack_scratch_failure33: ; preds = %helper_merge16 br label %stack_scratch_failure29 lookup_stack_scratch_merge34: ; preds = %helper_merge16 %probe_read_kernel36 = call i64 inttoptr (i64 113 to ptr)(ptr %lookup_stack_scratch_map32, i32 1016, ptr null) %get_stack39 = call i64 inttoptr (i64 67 to ptr)(ptr %0, ptr %lookup_stack_scratch_map32, i32 1016, i64 0) %29 = trunc i64 %get_stack39 to i32 %30 = icmp sge i32 %29, 0 br i1 %30, label %helper_merge41, label %helper_failure40 get_stack_success37: ; preds = %helper_merge41 %31 = udiv i64 %get_stack39, 8 %32 = getelementptr %kstack_key, ptr %stack_key28, i64 0, i32 1 store i64 %31, ptr %32, align 8 %33 = trunc i64 %31 to i8 %murmur_hash_253 = call i64 @murmur_hash_2(ptr %lookup_stack_scratch_map32, i8 %33, i64 1) %34 = getelementptr %kstack_key, ptr %stack_key28, i64 0, i32 0 store i64 %murmur_hash_253, ptr %34, align 8 %update_elem54 = call i64 inttoptr (i64 2 to ptr)(ptr @stack_bpftrace_127, ptr %stack_key28, ptr %lookup_stack_scratch_map32, i64 0) %35 = trunc i64 %update_elem54 to i32 %36 = icmp sge i32 %35, 0 br i1 %36, label %helper_merge56, label %helper_failure55 get_stack_fail38: ; preds = %helper_merge41 br label %merge_block30 helper_failure40: ; preds = %lookup_stack_scratch_merge34 call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t42) %37 = getelementptr %helper_error_t, ptr %helper_error_t42, i64 0, i32 0 store i64 30006, ptr %37, align 8 %38 = getelementptr %helper_error_t, ptr %helper_error_t42, i64 0, i32 1 store i64 3, ptr %38, align 8 %39 = getelementptr %helper_error_t, ptr %helper_error_t42, i64 0, i32 2 store i32 %29, ptr %39, align 4 %ringbuf_output43 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t42, i64 20, i64 0) %ringbuf_loss46 = icmp slt i64 %ringbuf_output43, 0 br i1 %ringbuf_loss46, label %event_loss_counter44, label %counter_merge45 helper_merge41: ; preds = %counter_merge45, %lookup_stack_scratch_merge34 %40 = icmp sge i64 %get_stack39, 0 br i1 %40, label %get_stack_success37, label %get_stack_fail38 event_loss_counter44: ; preds = %helper_failure40 call void @llvm.lifetime.start.p0(i64 -1, ptr %key47) store i32 0, ptr %key47, align 4 %lookup_elem48 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key47) %map_lookup_cond52 = icmp ne ptr %lookup_elem48, null br i1 %map_lookup_cond52, label %lookup_success49, label %lookup_failure50 counter_merge45: ; preds = %lookup_merge51, %helper_failure40 call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t42) br label %helper_merge41 lookup_success49: ; preds = %event_loss_counter44 %41 = atomicrmw add ptr %lookup_elem48, i64 1 seq_cst, align 8 br label %lookup_merge51 lookup_failure50: ; preds = %event_loss_counter44 br label %lookup_merge51 lookup_merge51: ; preds = %lookup_failure50, %lookup_success49 call void @llvm.lifetime.end.p0(i64 -1, ptr %key47) br label %counter_merge45 helper_failure55: ; preds = %get_stack_success37 call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t57) %42 = getelementptr %helper_error_t, ptr %helper_error_t57, i64 0, i32 0 store i64 30006, ptr %42, align 8 %43 = getelementptr %helper_error_t, ptr %helper_error_t57, i64 0, i32 1 store i64 4, ptr %43, align 8 %44 = getelementptr %helper_error_t, ptr %helper_error_t57, i64 0, i32 2 store i32 %35, ptr %44, align 4 %ringbuf_output58 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t57, i64 20, i64 0) %ringbuf_loss61 = icmp slt i64 %ringbuf_output58, 0 br i1 %ringbuf_loss61, label %event_loss_counter59, label %counter_merge60 helper_merge56: ; preds = %counter_merge60, %get_stack_success37 br label %merge_block30 event_loss_counter59: ; preds = %helper_failure55 call void @llvm.lifetime.start.p0(i64 -1, ptr %key62) store i32 0, ptr %key62, align 4 %lookup_elem63 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key62) %map_lookup_cond67 = icmp ne ptr %lookup_elem63, null br i1 %map_lookup_cond67, label %lookup_success64, label %lookup_failure65 counter_merge60: ; preds = %lookup_merge66, %helper_failure55 call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t57) br label %helper_merge56 lookup_success64: ; preds = %event_loss_counter59 %45 = atomicrmw add ptr %lookup_elem63, i64 1 seq_cst, align 8 br label %lookup_merge66 lookup_failure65: ; preds = %event_loss_counter59 br label %lookup_merge66 lookup_merge66: ; preds = %lookup_failure65, %lookup_success64 call void @llvm.lifetime.end.p0(i64 -1, ptr %key62) br label %counter_merge60 helper_failure69: ; preds = %merge_block30 call void @llvm.lifetime.start.p0(i64 -1, ptr %helper_error_t71) %46 = getelementptr %helper_error_t, ptr %helper_error_t71, i64 0, i32 0 store i64 30006, ptr %46, align 8 %47 = getelementptr %helper_error_t, ptr %helper_error_t71, i64 0, i32 1 store i64 5, ptr %47, align 8 %48 = getelementptr %helper_error_t, ptr %helper_error_t71, i64 0, i32 2 store i32 %27, ptr %48, align 4 %ringbuf_output72 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %helper_error_t71, i64 20, i64 0) %ringbuf_loss75 = icmp slt i64 %ringbuf_output72, 0 br i1 %ringbuf_loss75, label %event_loss_counter73, label %counter_merge74 helper_merge70: ; preds = %counter_merge74, %merge_block30 call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 event_loss_counter73: ; preds = %helper_failure69 call void @llvm.lifetime.start.p0(i64 -1, ptr %key76) store i32 0, ptr %key76, align 4 %lookup_elem77 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key76) %map_lookup_cond81 = icmp ne ptr %lookup_elem77, null br i1 %map_lookup_cond81, label %lookup_success78, label %lookup_failure79 counter_merge74: ; preds = %lookup_merge80, %helper_failure69 call void @llvm.lifetime.end.p0(i64 -1, ptr %helper_error_t71) br label %helper_merge70 lookup_success78: ; preds = %event_loss_counter73 %49 = atomicrmw add ptr %lookup_elem77, i64 1 seq_cst, align 8 br label %lookup_merge80 lookup_failure79: ; preds = %event_loss_counter73 br label %lookup_merge80 lookup_merge80: ; preds = %lookup_failure79, %lookup_success78 call void @llvm.lifetime.end.p0(i64 -1, ptr %key76) br label %counter_merge74 } ; Function Attrs: alwaysinline define internal i64 @murmur_hash_2(ptr %0, i8 %1, i64 %2) #1 section "helpers" { entry: %k = alloca i64, align 8 %i = alloca i8, align 1 %id = alloca i64, align 8 %seed_addr = alloca i64, align 8 %nr_stack_frames_addr = alloca i8, align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.start.p0(i64 -1, ptr %id) call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %k) store i8 %1, ptr %nr_stack_frames_addr, align 1 store i64 %2, ptr %seed_addr, align 8 %3 = load i8, ptr %nr_stack_frames_addr, align 1 %4 = zext i8 %3 to i64 %5 = mul i64 %4, -4132994306676758123 %6 = load i64, ptr %seed_addr, align 8 %7 = xor i64 %6, %5 store i64 %7, ptr %id, align 8 store i8 0, ptr %i, align 1 br label %while_cond while_cond: ; preds = %while_body, %entry %8 = load i8, ptr %nr_stack_frames_addr, align 1 %9 = load i8, ptr %i, align 1 %length.cmp = icmp ult i8 %9, %8 br i1 %length.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %10 = load i8, ptr %i, align 1 %11 = getelementptr i64, ptr %0, i8 %10 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %k, align 8 %13 = load i64, ptr %k, align 8 %14 = mul i64 %13, -4132994306676758123 store i64 %14, ptr %k, align 8 %15 = load i64, ptr %k, align 8 %16 = lshr i64 %15, 47 %17 = load i64, ptr %k, align 8 %18 = xor i64 %17, %16 store i64 %18, ptr %k, align 8 %19 = load i64, ptr %k, align 8 %20 = mul i64 %19, -4132994306676758123 store i64 %20, ptr %k, align 8 %21 = load i64, ptr %k, align 8 %22 = load i64, ptr %id, align 8 %23 = xor i64 %22, %21 store i64 %23, ptr %id, align 8 %24 = load i64, ptr %id, align 8 %25 = mul i64 %24, -4132994306676758123 store i64 %25, ptr %id, align 8 %26 = load i8, ptr %i, align 1 %27 = add i8 %26, 1 store i8 %27, ptr %i, align 1 br label %while_cond while_end: ; preds = %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %nr_stack_frames_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %seed_addr) call void @llvm.lifetime.end.p0(i64 -1, ptr %i) call void @llvm.lifetime.end.p0(i64 -1, ptr %k) %28 = load i64, ptr %id, align 8 %zero_cond = icmp eq i64 %28, 0 br i1 %zero_cond, label %if_zero, label %if_end if_zero: ; preds = %while_end store i64 1, ptr %id, align 8 br label %if_end if_end: ; preds = %if_zero, %while_end %29 = load i64, ptr %id, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %id) ret i64 %29 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { alwaysinline } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!90} !llvm.module.flags = !{!92} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 192, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 24, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !24) !24 = !{!5, !11, !12, !25} !25 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !26, size: 64, offset: 192) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 16, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "stack_bpftrace_127", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !39, !44, !49} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 288, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 9, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !40, size: 64, offset: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 4194304, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 131072, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 96, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 12, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !50, size: 64, offset: 192) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 8128, elements: !52) !52 = !{!53} !53 = !DISubrange(count: 127, lowerBound: 0) !54 = !DIGlobalVariableExpression(var: !55, expr: !DIExpression()) !55 = distinct !DIGlobalVariable(name: "stack_scratch", linkageName: "global", scope: !2, file: !2, type: !56, isLocal: false, isDefinition: true) !56 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !57) !57 = !{!58, !11, !63, !49} !58 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !59, size: 64) !59 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !60, size: 64) !60 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 192, elements: !61) !61 = !{!62} !62 = !DISubrange(count: 6, lowerBound: 0) !63 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !64, size: 64, offset: 128) !64 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !65, size: 64) !65 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !66 = !DIGlobalVariableExpression(var: !67, expr: !DIExpression()) !67 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !68, isLocal: false, isDefinition: true) !68 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !69) !69 = !{!70, !75} !70 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !71, size: 64) !71 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !72, size: 64) !72 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !73) !73 = !{!74} !74 = !DISubrange(count: 27, lowerBound: 0) !75 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !76, size: 64, offset: 64) !76 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !77, size: 64) !77 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !78) !78 = !{!79} !79 = !DISubrange(count: 262144, lowerBound: 0) !80 = !DIGlobalVariableExpression(var: !81, expr: !DIExpression()) !81 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !82, isLocal: false, isDefinition: true) !82 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !83) !83 = !{!84, !11, !63, !89} !84 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !85, size: 64) !85 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !86, size: 64) !86 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !87) !87 = !{!88} !88 = !DISubrange(count: 2, lowerBound: 0) !89 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !90 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !91) !91 = !{!0, !21, !30, !54, !66, !80} !92 = !{i32 2, !"Debug Info Version", i32 3} !93 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !94, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !90, retainedNodes: !97) !94 = !DISubroutineType(types: !95) !95 = !{!14, !96} !96 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !97 = !{!98} !98 = !DILocalVariable(name: "ctx", arg: 1, scope: !93, file: !2, type: !96) bpftrace-0.23.2/tests/codegen/llvm/self_probe.ll000066400000000000000000000133471477746507000216060ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @"self:signal:SIGUSR1" = global [20 x i8] c"self:signal:SIGUSR1\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @self_signal_SIGUSR1_1(ptr %0) section "s_self_signal_SIGUSR1_1" !dbg !51 { entry: %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr @"self:signal:SIGUSR1", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 160, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 20, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "self_signal_SIGUSR1_1", linkageName: "self_signal_SIGUSR1_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/str_scratch_buf.ll000066400000000000000000000136371477746507000226430ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @get_str_buf = dso_local externally_initialized global [1 x [1 x [64 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !38 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !49 { entry: %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [64 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 call void @llvm.memset.p0.i64(ptr align 1 %2, i8 0, i64 64, i1 false) %3 = call ptr @llvm.preserve.static.offset(ptr %0) %4 = getelementptr i64, ptr %3, i64 14 %arg0 = load volatile i64, ptr %4, align 8 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %2, i32 64, i64 %arg0) ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 512, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 512, elements: !28) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 512, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 64, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/str_stack.ll000066400000000000000000000124001477746507000214500ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %str = alloca [64 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %str) call void @llvm.memset.p0.i64(ptr align 1 %str, i8 0, i64 64, i1 false) %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %str, i32 64, i64 %arg0) call void @llvm.lifetime.end.p0(i64 -1, ptr %str) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/strcontains.ll000066400000000000000000000172441477746507000220350ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @hello-test-world = global [17 x i8] c"hello-test-world\00" @test = global [5 x i8] c"test\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_foo_1(ptr %0) section "s_kprobe_foo_1" !dbg !39 { entry: %strcontains.j = alloca i64, align 8 %strcontains.i = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %strcontains.i) store i64 0, ptr %strcontains.i, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %strcontains.j) br label %strcontains.empty.check strcontains.empty.check: ; preds = %entry %1 = load i8, ptr @test, align 1 %2 = icmp eq i8 %1, 0 br i1 %2, label %strcontains.true, label %strcontains.outer.cond strcontains.outer.cond: ; preds = %strcontains.outer.incr, %strcontains.empty.check %3 = load i64, ptr %strcontains.i, align 8 %4 = icmp ult i64 %3, 17 br i1 %4, label %strcontains.outer.cond.cmp, label %strcontains.false strcontains.outer.cond.cmp: ; preds = %strcontains.outer.cond %5 = getelementptr i8, ptr @hello-test-world, i64 %3 %6 = load i8, ptr %5, align 1 %7 = icmp ne i8 %6, 0 br i1 %7, label %strcontains.outer.body, label %strcontains.false strcontains.outer.body: ; preds = %strcontains.outer.cond.cmp store i64 0, ptr %strcontains.j, align 8 br label %strcontains.inner.cond strcontains.inner.cond: ; preds = %strcontains.inner.incr, %strcontains.outer.body %8 = load i64, ptr %strcontains.j, align 8 %9 = icmp ult i64 %8, 5 br i1 %9, label %strcontains.inner.body, label %strcontains.outer.incr strcontains.inner.body: ; preds = %strcontains.inner.cond %10 = getelementptr i8, ptr @test, i64 %8 %11 = load i8, ptr %10, align 1 %12 = icmp eq i8 %11, 0 br i1 %12, label %strcontains.true, label %strcontains.inner.notnull strcontains.inner.notnull: ; preds = %strcontains.inner.body %13 = add i64 %3, %8 %14 = icmp uge i64 %13, 17 br i1 %14, label %strcontains.outer.incr, label %strcontains.inner.cmp strcontains.inner.cmp: ; preds = %strcontains.inner.notnull %15 = getelementptr i8, ptr @hello-test-world, i64 %13 %16 = load i8, ptr %15, align 1 %17 = icmp ne i8 %16, %11 br i1 %17, label %strcontains.outer.incr, label %strcontains.inner.incr strcontains.inner.incr: ; preds = %strcontains.inner.cmp %18 = load i64, ptr %strcontains.j, align 8 %19 = add i64 %18, 1 store i64 %19, ptr %strcontains.j, align 8 br label %strcontains.inner.cond strcontains.outer.incr: ; preds = %strcontains.inner.cmp, %strcontains.inner.notnull, %strcontains.inner.cond %20 = load i64, ptr %strcontains.i, align 8 %21 = add i64 %20, 1 store i64 %21, ptr %strcontains.i, align 8 br label %strcontains.outer.cond strcontains.true: ; preds = %strcontains.inner.body, %strcontains.empty.check br label %strcontains.done strcontains.false: ; preds = %strcontains.outer.cond.cmp, %strcontains.outer.cond br label %strcontains.done strcontains.done: ; preds = %strcontains.true, %strcontains.false %result = phi i1 [ false, %strcontains.false ], [ true, %strcontains.true ] call void @llvm.lifetime.end.p0(i64 -1, ptr %strcontains.j) call void @llvm.lifetime.end.p0(i64 -1, ptr %strcontains.i) %22 = zext i1 %result to i64 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_foo_1", linkageName: "kprobe_foo_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/strcontains_no_literals.ll000066400000000000000000000236411477746507000244260ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @get_str_buf = dso_local externally_initialized global [1 x [2 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !38 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_foo_1(ptr %0) section "s_kprobe_foo_1" !dbg !49 { entry: %strcontains.j = alloca i64, align 8 %strcontains.i = alloca i64, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [2 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %3 = call ptr @llvm.preserve.static.offset(ptr %0) %4 = getelementptr i64, ptr %3, i64 14 %arg0 = load volatile i64, ptr %4, align 8 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %2, i32 1024, i64 %arg0) %get_cpu_id1 = call i64 inttoptr (i64 8 to ptr)() %5 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded2 = and i64 %get_cpu_id1, %5 %6 = getelementptr [1 x [2 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded2, i64 1, i64 0 %probe_read_kernel3 = call i64 inttoptr (i64 113 to ptr)(ptr %6, i32 1024, ptr null) %7 = call ptr @llvm.preserve.static.offset(ptr %0) %8 = getelementptr i64, ptr %7, i64 13 %arg1 = load volatile i64, ptr %8, align 8 %probe_read_kernel_str4 = call i64 inttoptr (i64 115 to ptr)(ptr %6, i32 1024, i64 %arg1) call void @llvm.lifetime.start.p0(i64 -1, ptr %strcontains.i) store i64 0, ptr %strcontains.i, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %strcontains.j) br label %strcontains.empty.check strcontains.empty.check: ; preds = %entry %9 = getelementptr i8, ptr %6, i64 0 %10 = load i8, ptr %9, align 1 %11 = icmp eq i8 %10, 0 br i1 %11, label %strcontains.true, label %strcontains.outer.cond strcontains.outer.cond: ; preds = %strcontains.outer.incr, %strcontains.empty.check %12 = load i64, ptr %strcontains.i, align 8 %13 = icmp ult i64 %12, 1024 br i1 %13, label %strcontains.outer.cond.cmp, label %strcontains.false strcontains.outer.cond.cmp: ; preds = %strcontains.outer.cond %14 = getelementptr i8, ptr %2, i64 %12 %15 = load i8, ptr %14, align 1 %16 = icmp ne i8 %15, 0 br i1 %16, label %strcontains.outer.body, label %strcontains.false strcontains.outer.body: ; preds = %strcontains.outer.cond.cmp store i64 0, ptr %strcontains.j, align 8 br label %strcontains.inner.cond strcontains.inner.cond: ; preds = %strcontains.inner.incr, %strcontains.outer.body %17 = load i64, ptr %strcontains.j, align 8 %18 = icmp ult i64 %17, 1024 br i1 %18, label %strcontains.inner.body, label %strcontains.outer.incr strcontains.inner.body: ; preds = %strcontains.inner.cond %19 = getelementptr i8, ptr %6, i64 %17 %20 = load i8, ptr %19, align 1 %21 = icmp eq i8 %20, 0 br i1 %21, label %strcontains.true, label %strcontains.inner.notnull strcontains.inner.notnull: ; preds = %strcontains.inner.body %22 = add i64 %12, %17 %23 = icmp uge i64 %22, 1024 br i1 %23, label %strcontains.outer.incr, label %strcontains.inner.cmp strcontains.inner.cmp: ; preds = %strcontains.inner.notnull %24 = getelementptr i8, ptr %2, i64 %22 %25 = load i8, ptr %24, align 1 %26 = icmp ne i8 %25, %20 br i1 %26, label %strcontains.outer.incr, label %strcontains.inner.incr strcontains.inner.incr: ; preds = %strcontains.inner.cmp %27 = load i64, ptr %strcontains.j, align 8 %28 = add i64 %27, 1 store i64 %28, ptr %strcontains.j, align 8 br label %strcontains.inner.cond strcontains.outer.incr: ; preds = %strcontains.inner.cmp, %strcontains.inner.notnull, %strcontains.inner.cond %29 = load i64, ptr %strcontains.i, align 8 %30 = add i64 %29, 1 store i64 %30, ptr %strcontains.i, align 8 br label %strcontains.outer.cond strcontains.true: ; preds = %strcontains.inner.body, %strcontains.empty.check br label %strcontains.done strcontains.false: ; preds = %strcontains.outer.cond.cmp, %strcontains.outer.cond br label %strcontains.done strcontains.done: ; preds = %strcontains.true, %strcontains.false %result = phi i1 [ false, %strcontains.false ], [ true, %strcontains.true ] call void @llvm.lifetime.end.p0(i64 -1, ptr %strcontains.j) call void @llvm.lifetime.end.p0(i64 -1, ptr %strcontains.i) %31 = zext i1 %result to i64 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 16384, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 16384, elements: !23) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 8192, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 1024, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "kprobe_foo_1", linkageName: "kprobe_foo_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/strcontains_one_literal.ll000066400000000000000000000225361477746507000244120ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !38 @test = global [5 x i8] c"test\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_foo_1(ptr %0) section "s_kprobe_foo_1" !dbg !49 { entry: %strcontains.j = alloca i64, align 8 %strcontains.i = alloca i64, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %3 = call ptr @llvm.preserve.static.offset(ptr %0) %4 = getelementptr i64, ptr %3, i64 14 %arg0 = load volatile i64, ptr %4, align 8 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %2, i32 1024, i64 %arg0) call void @llvm.lifetime.start.p0(i64 -1, ptr %strcontains.i) store i64 0, ptr %strcontains.i, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %strcontains.j) br label %strcontains.empty.check strcontains.empty.check: ; preds = %entry %5 = load i8, ptr @test, align 1 %6 = icmp eq i8 %5, 0 br i1 %6, label %strcontains.true, label %strcontains.outer.cond strcontains.outer.cond: ; preds = %strcontains.outer.incr, %strcontains.empty.check %7 = load i64, ptr %strcontains.i, align 8 %8 = icmp ult i64 %7, 1024 br i1 %8, label %strcontains.outer.cond.cmp, label %strcontains.false strcontains.outer.cond.cmp: ; preds = %strcontains.outer.cond %9 = getelementptr i8, ptr %2, i64 %7 %10 = load i8, ptr %9, align 1 %11 = icmp ne i8 %10, 0 br i1 %11, label %strcontains.outer.body, label %strcontains.false strcontains.outer.body: ; preds = %strcontains.outer.cond.cmp store i64 0, ptr %strcontains.j, align 8 br label %strcontains.inner.cond strcontains.inner.cond: ; preds = %strcontains.inner.incr, %strcontains.outer.body %12 = load i64, ptr %strcontains.j, align 8 %13 = icmp ult i64 %12, 5 br i1 %13, label %strcontains.inner.body, label %strcontains.outer.incr strcontains.inner.body: ; preds = %strcontains.inner.cond %14 = getelementptr i8, ptr @test, i64 %12 %15 = load i8, ptr %14, align 1 %16 = icmp eq i8 %15, 0 br i1 %16, label %strcontains.true, label %strcontains.inner.notnull strcontains.inner.notnull: ; preds = %strcontains.inner.body %17 = add i64 %7, %12 %18 = icmp uge i64 %17, 1024 br i1 %18, label %strcontains.outer.incr, label %strcontains.inner.cmp strcontains.inner.cmp: ; preds = %strcontains.inner.notnull %19 = getelementptr i8, ptr %2, i64 %17 %20 = load i8, ptr %19, align 1 %21 = icmp ne i8 %20, %15 br i1 %21, label %strcontains.outer.incr, label %strcontains.inner.incr strcontains.inner.incr: ; preds = %strcontains.inner.cmp %22 = load i64, ptr %strcontains.j, align 8 %23 = add i64 %22, 1 store i64 %23, ptr %strcontains.j, align 8 br label %strcontains.inner.cond strcontains.outer.incr: ; preds = %strcontains.inner.cmp, %strcontains.inner.notnull, %strcontains.inner.cond %24 = load i64, ptr %strcontains.i, align 8 %25 = add i64 %24, 1 store i64 %25, ptr %strcontains.i, align 8 br label %strcontains.outer.cond strcontains.true: ; preds = %strcontains.inner.body, %strcontains.empty.check br label %strcontains.done strcontains.false: ; preds = %strcontains.outer.cond.cmp, %strcontains.outer.cond br label %strcontains.done strcontains.done: ; preds = %strcontains.true, %strcontains.false %result = phi i1 [ false, %strcontains.false ], [ true, %strcontains.true ] call void @llvm.lifetime.end.p0(i64 -1, ptr %strcontains.j) call void @llvm.lifetime.end.p0(i64 -1, ptr %strcontains.i) %26 = zext i1 %result to i64 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 8192, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 8192, elements: !28) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 8192, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 1024, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "kprobe_foo_1", linkageName: "kprobe_foo_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/string_equal_comparison.ll000066400000000000000000000230311477746507000244040ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 @sshd = global [5 x i8] c"sshd\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %strcmp.result = alloca i1, align 1 %comm = alloca [16 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %comm) call void @llvm.memset.p0.i64(ptr align 1 %comm, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to ptr)(ptr %comm, i64 16) call void @llvm.lifetime.start.p0(i64 -1, ptr %strcmp.result) store i1 false, ptr %strcmp.result, align 1 %1 = getelementptr i8, ptr %comm, i32 0 %2 = load i8, ptr %1, align 1 %3 = load i8, ptr @sshd, align 1 %strcmp.cmp = icmp ne i8 %2, %3 br i1 %strcmp.cmp, label %strcmp.false, label %strcmp.loop_null_cmp strcmp.false: ; preds = %strcmp.done, %strcmp.loop9, %strcmp.loop5, %strcmp.loop1, %strcmp.loop, %entry %4 = load i1, ptr %strcmp.result, align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %strcmp.result) %5 = zext i1 %4 to i64 call void @llvm.lifetime.end.p0(i64 -1, ptr %comm) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %5, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 strcmp.done: ; preds = %strcmp.loop13, %strcmp.loop_null_cmp14, %strcmp.loop_null_cmp10, %strcmp.loop_null_cmp6, %strcmp.loop_null_cmp2, %strcmp.loop_null_cmp store i1 true, ptr %strcmp.result, align 1 br label %strcmp.false strcmp.loop: ; preds = %strcmp.loop_null_cmp %6 = getelementptr i8, ptr %comm, i32 1 %7 = load i8, ptr %6, align 1 %8 = load i8, ptr getelementptr (i8, ptr @sshd, i32 1), align 1 %strcmp.cmp3 = icmp ne i8 %7, %8 br i1 %strcmp.cmp3, label %strcmp.false, label %strcmp.loop_null_cmp2 strcmp.loop_null_cmp: ; preds = %entry %strcmp.cmp_null = icmp eq i8 %2, 0 br i1 %strcmp.cmp_null, label %strcmp.done, label %strcmp.loop strcmp.loop1: ; preds = %strcmp.loop_null_cmp2 %9 = getelementptr i8, ptr %comm, i32 2 %10 = load i8, ptr %9, align 1 %11 = load i8, ptr getelementptr (i8, ptr @sshd, i32 2), align 1 %strcmp.cmp7 = icmp ne i8 %10, %11 br i1 %strcmp.cmp7, label %strcmp.false, label %strcmp.loop_null_cmp6 strcmp.loop_null_cmp2: ; preds = %strcmp.loop %strcmp.cmp_null4 = icmp eq i8 %7, 0 br i1 %strcmp.cmp_null4, label %strcmp.done, label %strcmp.loop1 strcmp.loop5: ; preds = %strcmp.loop_null_cmp6 %12 = getelementptr i8, ptr %comm, i32 3 %13 = load i8, ptr %12, align 1 %14 = load i8, ptr getelementptr (i8, ptr @sshd, i32 3), align 1 %strcmp.cmp11 = icmp ne i8 %13, %14 br i1 %strcmp.cmp11, label %strcmp.false, label %strcmp.loop_null_cmp10 strcmp.loop_null_cmp6: ; preds = %strcmp.loop1 %strcmp.cmp_null8 = icmp eq i8 %10, 0 br i1 %strcmp.cmp_null8, label %strcmp.done, label %strcmp.loop5 strcmp.loop9: ; preds = %strcmp.loop_null_cmp10 %15 = getelementptr i8, ptr %comm, i32 4 %16 = load i8, ptr %15, align 1 %17 = load i8, ptr getelementptr (i8, ptr @sshd, i32 4), align 1 %strcmp.cmp15 = icmp ne i8 %16, %17 br i1 %strcmp.cmp15, label %strcmp.false, label %strcmp.loop_null_cmp14 strcmp.loop_null_cmp10: ; preds = %strcmp.loop5 %strcmp.cmp_null12 = icmp eq i8 %13, 0 br i1 %strcmp.cmp_null12, label %strcmp.done, label %strcmp.loop9 strcmp.loop13: ; preds = %strcmp.loop_null_cmp14 br label %strcmp.done strcmp.loop_null_cmp14: ; preds = %strcmp.loop9 %strcmp.cmp_null16 = icmp eq i8 %16, 0 br i1 %strcmp.cmp_null16, label %strcmp.done, label %strcmp.loop13 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/string_not_equal_comparison.ll000066400000000000000000000230311477746507000252640ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 @sshd = global [5 x i8] c"sshd\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %strcmp.result = alloca i1, align 1 %comm = alloca [16 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %comm) call void @llvm.memset.p0.i64(ptr align 1 %comm, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to ptr)(ptr %comm, i64 16) call void @llvm.lifetime.start.p0(i64 -1, ptr %strcmp.result) store i1 true, ptr %strcmp.result, align 1 %1 = getelementptr i8, ptr %comm, i32 0 %2 = load i8, ptr %1, align 1 %3 = load i8, ptr @sshd, align 1 %strcmp.cmp = icmp ne i8 %2, %3 br i1 %strcmp.cmp, label %strcmp.false, label %strcmp.loop_null_cmp strcmp.false: ; preds = %strcmp.done, %strcmp.loop9, %strcmp.loop5, %strcmp.loop1, %strcmp.loop, %entry %4 = load i1, ptr %strcmp.result, align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %strcmp.result) %5 = zext i1 %4 to i64 call void @llvm.lifetime.end.p0(i64 -1, ptr %comm) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %5, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 strcmp.done: ; preds = %strcmp.loop13, %strcmp.loop_null_cmp14, %strcmp.loop_null_cmp10, %strcmp.loop_null_cmp6, %strcmp.loop_null_cmp2, %strcmp.loop_null_cmp store i1 false, ptr %strcmp.result, align 1 br label %strcmp.false strcmp.loop: ; preds = %strcmp.loop_null_cmp %6 = getelementptr i8, ptr %comm, i32 1 %7 = load i8, ptr %6, align 1 %8 = load i8, ptr getelementptr (i8, ptr @sshd, i32 1), align 1 %strcmp.cmp3 = icmp ne i8 %7, %8 br i1 %strcmp.cmp3, label %strcmp.false, label %strcmp.loop_null_cmp2 strcmp.loop_null_cmp: ; preds = %entry %strcmp.cmp_null = icmp eq i8 %2, 0 br i1 %strcmp.cmp_null, label %strcmp.done, label %strcmp.loop strcmp.loop1: ; preds = %strcmp.loop_null_cmp2 %9 = getelementptr i8, ptr %comm, i32 2 %10 = load i8, ptr %9, align 1 %11 = load i8, ptr getelementptr (i8, ptr @sshd, i32 2), align 1 %strcmp.cmp7 = icmp ne i8 %10, %11 br i1 %strcmp.cmp7, label %strcmp.false, label %strcmp.loop_null_cmp6 strcmp.loop_null_cmp2: ; preds = %strcmp.loop %strcmp.cmp_null4 = icmp eq i8 %7, 0 br i1 %strcmp.cmp_null4, label %strcmp.done, label %strcmp.loop1 strcmp.loop5: ; preds = %strcmp.loop_null_cmp6 %12 = getelementptr i8, ptr %comm, i32 3 %13 = load i8, ptr %12, align 1 %14 = load i8, ptr getelementptr (i8, ptr @sshd, i32 3), align 1 %strcmp.cmp11 = icmp ne i8 %13, %14 br i1 %strcmp.cmp11, label %strcmp.false, label %strcmp.loop_null_cmp10 strcmp.loop_null_cmp6: ; preds = %strcmp.loop1 %strcmp.cmp_null8 = icmp eq i8 %10, 0 br i1 %strcmp.cmp_null8, label %strcmp.done, label %strcmp.loop5 strcmp.loop9: ; preds = %strcmp.loop_null_cmp10 %15 = getelementptr i8, ptr %comm, i32 4 %16 = load i8, ptr %15, align 1 %17 = load i8, ptr getelementptr (i8, ptr @sshd, i32 4), align 1 %strcmp.cmp15 = icmp ne i8 %16, %17 br i1 %strcmp.cmp15, label %strcmp.false, label %strcmp.loop_null_cmp14 strcmp.loop_null_cmp10: ; preds = %strcmp.loop5 %strcmp.cmp_null12 = icmp eq i8 %13, 0 br i1 %strcmp.cmp_null12, label %strcmp.done, label %strcmp.loop9 strcmp.loop13: ; preds = %strcmp.loop_null_cmp14 br label %strcmp.done strcmp.loop_null_cmp14: ; preds = %strcmp.loop9 %strcmp.cmp_null16 = icmp eq i8 %16, 0 br i1 %strcmp.cmp_null16, label %strcmp.done, label %strcmp.loop13 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/string_propagation.ll000066400000000000000000000174741477746507000234040ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !23 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !37 @asdf = global [5 x i8] c"asdf\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !53 { entry: %"@y_key" = alloca i64, align 8 %lookup_elem_val = alloca [5 x i8], align 1 %"@x_key1" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr @asdf, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.memcpy.p0.p0.i64(ptr align 1 %lookup_elem_val, ptr align 1 %lookup_elem, i64 5, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.memset.p0.i64(ptr align 1 %lookup_elem_val, i8 0, i64 5, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %lookup_elem_val, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!50} !llvm.module.flags = !{!52} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 40, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 5, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !23 = !DIGlobalVariableExpression(var: !24, expr: !DIExpression()) !24 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !25, isLocal: false, isDefinition: true) !25 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !26) !26 = !{!27, !32} !27 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !28, size: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 27, lowerBound: 0) !32 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !33, size: 64, offset: 64) !33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !34, size: 64) !34 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !35) !35 = !{!36} !36 = !DISubrange(count: 262144, lowerBound: 0) !37 = !DIGlobalVariableExpression(var: !38, expr: !DIExpression()) !38 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !39, isLocal: false, isDefinition: true) !39 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !40) !40 = !{!41, !11, !46, !49} !41 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !42, size: 64) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !44) !44 = !{!45} !45 = !DISubrange(count: 2, lowerBound: 0) !46 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !47, size: 64, offset: 128) !47 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !48, size: 64) !48 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !49 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !50 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !51) !51 = !{!0, !21, !23, !37} !52 = !{i32 2, !"Debug Info Version", i32 3} !53 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !54, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !50, retainedNodes: !57) !54 = !DISubroutineType(types: !55) !55 = !{!14, !56} !56 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !57 = !{!58} !58 = !DILocalVariable(name: "ctx", arg: 1, scope: !53, file: !2, type: !56) bpftrace-0.23.2/tests/codegen/llvm/strncmp_no_literals.ll000066400000000000000000000427431477746507000235510ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !42 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !44 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @tracepoint_file_filename_1(ptr %0) section "s_tracepoint_file_filename_1" !dbg !55 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %strcmp.result = alloca i1, align 1 %comm = alloca [16 x i8], align 1 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %3 = call ptr @llvm.preserve.static.offset(ptr %0) %4 = getelementptr i8, ptr %3, i64 8 %5 = load volatile i64, ptr %4, align 8 %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %2, i32 1024, i64 %5) call void @llvm.lifetime.start.p0(i64 -1, ptr %comm) call void @llvm.memset.p0.i64(ptr align 1 %comm, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to ptr)(ptr %comm, i64 16) call void @llvm.lifetime.start.p0(i64 -1, ptr %strcmp.result) store i1 false, ptr %strcmp.result, align 1 %6 = getelementptr i8, ptr %2, i32 0 %7 = load i8, ptr %6, align 1 %8 = getelementptr i8, ptr %comm, i32 0 %9 = load i8, ptr %8, align 1 %strcmp.cmp = icmp ne i8 %7, %9 br i1 %strcmp.cmp, label %strcmp.false, label %strcmp.loop_null_cmp pred_false: ; preds = %strcmp.false ret i64 1 pred_true: ; preds = %strcmp.false call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 1 strcmp.false: ; preds = %strcmp.done, %strcmp.loop53, %strcmp.loop49, %strcmp.loop45, %strcmp.loop41, %strcmp.loop37, %strcmp.loop33, %strcmp.loop29, %strcmp.loop25, %strcmp.loop21, %strcmp.loop17, %strcmp.loop13, %strcmp.loop9, %strcmp.loop5, %strcmp.loop1, %strcmp.loop, %entry %10 = load i1, ptr %strcmp.result, align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %strcmp.result) %11 = zext i1 %10 to i64 call void @llvm.lifetime.end.p0(i64 -1, ptr %comm) %predcond = icmp eq i64 %11, 0 br i1 %predcond, label %pred_false, label %pred_true strcmp.done: ; preds = %strcmp.loop57, %strcmp.loop_null_cmp58, %strcmp.loop_null_cmp54, %strcmp.loop_null_cmp50, %strcmp.loop_null_cmp46, %strcmp.loop_null_cmp42, %strcmp.loop_null_cmp38, %strcmp.loop_null_cmp34, %strcmp.loop_null_cmp30, %strcmp.loop_null_cmp26, %strcmp.loop_null_cmp22, %strcmp.loop_null_cmp18, %strcmp.loop_null_cmp14, %strcmp.loop_null_cmp10, %strcmp.loop_null_cmp6, %strcmp.loop_null_cmp2, %strcmp.loop_null_cmp store i1 true, ptr %strcmp.result, align 1 br label %strcmp.false strcmp.loop: ; preds = %strcmp.loop_null_cmp %12 = getelementptr i8, ptr %2, i32 1 %13 = load i8, ptr %12, align 1 %14 = getelementptr i8, ptr %comm, i32 1 %15 = load i8, ptr %14, align 1 %strcmp.cmp3 = icmp ne i8 %13, %15 br i1 %strcmp.cmp3, label %strcmp.false, label %strcmp.loop_null_cmp2 strcmp.loop_null_cmp: ; preds = %entry %strcmp.cmp_null = icmp eq i8 %7, 0 br i1 %strcmp.cmp_null, label %strcmp.done, label %strcmp.loop strcmp.loop1: ; preds = %strcmp.loop_null_cmp2 %16 = getelementptr i8, ptr %2, i32 2 %17 = load i8, ptr %16, align 1 %18 = getelementptr i8, ptr %comm, i32 2 %19 = load i8, ptr %18, align 1 %strcmp.cmp7 = icmp ne i8 %17, %19 br i1 %strcmp.cmp7, label %strcmp.false, label %strcmp.loop_null_cmp6 strcmp.loop_null_cmp2: ; preds = %strcmp.loop %strcmp.cmp_null4 = icmp eq i8 %13, 0 br i1 %strcmp.cmp_null4, label %strcmp.done, label %strcmp.loop1 strcmp.loop5: ; preds = %strcmp.loop_null_cmp6 %20 = getelementptr i8, ptr %2, i32 3 %21 = load i8, ptr %20, align 1 %22 = getelementptr i8, ptr %comm, i32 3 %23 = load i8, ptr %22, align 1 %strcmp.cmp11 = icmp ne i8 %21, %23 br i1 %strcmp.cmp11, label %strcmp.false, label %strcmp.loop_null_cmp10 strcmp.loop_null_cmp6: ; preds = %strcmp.loop1 %strcmp.cmp_null8 = icmp eq i8 %17, 0 br i1 %strcmp.cmp_null8, label %strcmp.done, label %strcmp.loop5 strcmp.loop9: ; preds = %strcmp.loop_null_cmp10 %24 = getelementptr i8, ptr %2, i32 4 %25 = load i8, ptr %24, align 1 %26 = getelementptr i8, ptr %comm, i32 4 %27 = load i8, ptr %26, align 1 %strcmp.cmp15 = icmp ne i8 %25, %27 br i1 %strcmp.cmp15, label %strcmp.false, label %strcmp.loop_null_cmp14 strcmp.loop_null_cmp10: ; preds = %strcmp.loop5 %strcmp.cmp_null12 = icmp eq i8 %21, 0 br i1 %strcmp.cmp_null12, label %strcmp.done, label %strcmp.loop9 strcmp.loop13: ; preds = %strcmp.loop_null_cmp14 %28 = getelementptr i8, ptr %2, i32 5 %29 = load i8, ptr %28, align 1 %30 = getelementptr i8, ptr %comm, i32 5 %31 = load i8, ptr %30, align 1 %strcmp.cmp19 = icmp ne i8 %29, %31 br i1 %strcmp.cmp19, label %strcmp.false, label %strcmp.loop_null_cmp18 strcmp.loop_null_cmp14: ; preds = %strcmp.loop9 %strcmp.cmp_null16 = icmp eq i8 %25, 0 br i1 %strcmp.cmp_null16, label %strcmp.done, label %strcmp.loop13 strcmp.loop17: ; preds = %strcmp.loop_null_cmp18 %32 = getelementptr i8, ptr %2, i32 6 %33 = load i8, ptr %32, align 1 %34 = getelementptr i8, ptr %comm, i32 6 %35 = load i8, ptr %34, align 1 %strcmp.cmp23 = icmp ne i8 %33, %35 br i1 %strcmp.cmp23, label %strcmp.false, label %strcmp.loop_null_cmp22 strcmp.loop_null_cmp18: ; preds = %strcmp.loop13 %strcmp.cmp_null20 = icmp eq i8 %29, 0 br i1 %strcmp.cmp_null20, label %strcmp.done, label %strcmp.loop17 strcmp.loop21: ; preds = %strcmp.loop_null_cmp22 %36 = getelementptr i8, ptr %2, i32 7 %37 = load i8, ptr %36, align 1 %38 = getelementptr i8, ptr %comm, i32 7 %39 = load i8, ptr %38, align 1 %strcmp.cmp27 = icmp ne i8 %37, %39 br i1 %strcmp.cmp27, label %strcmp.false, label %strcmp.loop_null_cmp26 strcmp.loop_null_cmp22: ; preds = %strcmp.loop17 %strcmp.cmp_null24 = icmp eq i8 %33, 0 br i1 %strcmp.cmp_null24, label %strcmp.done, label %strcmp.loop21 strcmp.loop25: ; preds = %strcmp.loop_null_cmp26 %40 = getelementptr i8, ptr %2, i32 8 %41 = load i8, ptr %40, align 1 %42 = getelementptr i8, ptr %comm, i32 8 %43 = load i8, ptr %42, align 1 %strcmp.cmp31 = icmp ne i8 %41, %43 br i1 %strcmp.cmp31, label %strcmp.false, label %strcmp.loop_null_cmp30 strcmp.loop_null_cmp26: ; preds = %strcmp.loop21 %strcmp.cmp_null28 = icmp eq i8 %37, 0 br i1 %strcmp.cmp_null28, label %strcmp.done, label %strcmp.loop25 strcmp.loop29: ; preds = %strcmp.loop_null_cmp30 %44 = getelementptr i8, ptr %2, i32 9 %45 = load i8, ptr %44, align 1 %46 = getelementptr i8, ptr %comm, i32 9 %47 = load i8, ptr %46, align 1 %strcmp.cmp35 = icmp ne i8 %45, %47 br i1 %strcmp.cmp35, label %strcmp.false, label %strcmp.loop_null_cmp34 strcmp.loop_null_cmp30: ; preds = %strcmp.loop25 %strcmp.cmp_null32 = icmp eq i8 %41, 0 br i1 %strcmp.cmp_null32, label %strcmp.done, label %strcmp.loop29 strcmp.loop33: ; preds = %strcmp.loop_null_cmp34 %48 = getelementptr i8, ptr %2, i32 10 %49 = load i8, ptr %48, align 1 %50 = getelementptr i8, ptr %comm, i32 10 %51 = load i8, ptr %50, align 1 %strcmp.cmp39 = icmp ne i8 %49, %51 br i1 %strcmp.cmp39, label %strcmp.false, label %strcmp.loop_null_cmp38 strcmp.loop_null_cmp34: ; preds = %strcmp.loop29 %strcmp.cmp_null36 = icmp eq i8 %45, 0 br i1 %strcmp.cmp_null36, label %strcmp.done, label %strcmp.loop33 strcmp.loop37: ; preds = %strcmp.loop_null_cmp38 %52 = getelementptr i8, ptr %2, i32 11 %53 = load i8, ptr %52, align 1 %54 = getelementptr i8, ptr %comm, i32 11 %55 = load i8, ptr %54, align 1 %strcmp.cmp43 = icmp ne i8 %53, %55 br i1 %strcmp.cmp43, label %strcmp.false, label %strcmp.loop_null_cmp42 strcmp.loop_null_cmp38: ; preds = %strcmp.loop33 %strcmp.cmp_null40 = icmp eq i8 %49, 0 br i1 %strcmp.cmp_null40, label %strcmp.done, label %strcmp.loop37 strcmp.loop41: ; preds = %strcmp.loop_null_cmp42 %56 = getelementptr i8, ptr %2, i32 12 %57 = load i8, ptr %56, align 1 %58 = getelementptr i8, ptr %comm, i32 12 %59 = load i8, ptr %58, align 1 %strcmp.cmp47 = icmp ne i8 %57, %59 br i1 %strcmp.cmp47, label %strcmp.false, label %strcmp.loop_null_cmp46 strcmp.loop_null_cmp42: ; preds = %strcmp.loop37 %strcmp.cmp_null44 = icmp eq i8 %53, 0 br i1 %strcmp.cmp_null44, label %strcmp.done, label %strcmp.loop41 strcmp.loop45: ; preds = %strcmp.loop_null_cmp46 %60 = getelementptr i8, ptr %2, i32 13 %61 = load i8, ptr %60, align 1 %62 = getelementptr i8, ptr %comm, i32 13 %63 = load i8, ptr %62, align 1 %strcmp.cmp51 = icmp ne i8 %61, %63 br i1 %strcmp.cmp51, label %strcmp.false, label %strcmp.loop_null_cmp50 strcmp.loop_null_cmp46: ; preds = %strcmp.loop41 %strcmp.cmp_null48 = icmp eq i8 %57, 0 br i1 %strcmp.cmp_null48, label %strcmp.done, label %strcmp.loop45 strcmp.loop49: ; preds = %strcmp.loop_null_cmp50 %64 = getelementptr i8, ptr %2, i32 14 %65 = load i8, ptr %64, align 1 %66 = getelementptr i8, ptr %comm, i32 14 %67 = load i8, ptr %66, align 1 %strcmp.cmp55 = icmp ne i8 %65, %67 br i1 %strcmp.cmp55, label %strcmp.false, label %strcmp.loop_null_cmp54 strcmp.loop_null_cmp50: ; preds = %strcmp.loop45 %strcmp.cmp_null52 = icmp eq i8 %61, 0 br i1 %strcmp.cmp_null52, label %strcmp.done, label %strcmp.loop49 strcmp.loop53: ; preds = %strcmp.loop_null_cmp54 %68 = getelementptr i8, ptr %2, i32 15 %69 = load i8, ptr %68, align 1 %70 = getelementptr i8, ptr %comm, i32 15 %71 = load i8, ptr %70, align 1 %strcmp.cmp59 = icmp ne i8 %69, %71 br i1 %strcmp.cmp59, label %strcmp.false, label %strcmp.loop_null_cmp58 strcmp.loop_null_cmp54: ; preds = %strcmp.loop49 %strcmp.cmp_null56 = icmp eq i8 %65, 0 br i1 %strcmp.cmp_null56, label %strcmp.done, label %strcmp.loop53 strcmp.loop57: ; preds = %strcmp.loop_null_cmp58 br label %strcmp.done strcmp.loop_null_cmp58: ; preds = %strcmp.loop53 %strcmp.cmp_null60 = icmp eq i8 %69, 0 br i1 %strcmp.cmp_null60, label %strcmp.done, label %strcmp.loop57 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!52} !llvm.module.flags = !{!54} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = !DIGlobalVariableExpression(var: !43, expr: !DIExpression()) !43 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !44 = !DIGlobalVariableExpression(var: !45, expr: !DIExpression()) !45 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !46, isLocal: false, isDefinition: true) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !47, size: 8192, elements: !9) !47 = !DICompositeType(tag: DW_TAG_array_type, baseType: !48, size: 8192, elements: !9) !48 = !DICompositeType(tag: DW_TAG_array_type, baseType: !49, size: 8192, elements: !50) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DISubrange(count: 1024, lowerBound: 0) !52 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !53) !53 = !{!0, !16, !30, !42, !44} !54 = !{i32 2, !"Debug Info Version", i32 3} !55 = distinct !DISubprogram(name: "tracepoint_file_filename_1", linkageName: "tracepoint_file_filename_1", scope: !2, file: !2, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !52, retainedNodes: !59) !56 = !DISubroutineType(types: !57) !57 = !{!14, !58} !58 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !55, file: !2, type: !58) bpftrace-0.23.2/tests/codegen/llvm/strncmp_one_literal.ll000066400000000000000000000176011477746507000235260ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 @sshd = global [5 x i8] c"sshd\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !50 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %strcmp.result = alloca i1, align 1 %comm = alloca [16 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %comm) call void @llvm.memset.p0.i64(ptr align 1 %comm, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to ptr)(ptr %comm, i64 16) call void @llvm.lifetime.start.p0(i64 -1, ptr %strcmp.result) store i1 true, ptr %strcmp.result, align 1 %1 = getelementptr i8, ptr %comm, i32 0 %2 = load i8, ptr %1, align 1 %3 = load i8, ptr @sshd, align 1 %strcmp.cmp = icmp ne i8 %2, %3 br i1 %strcmp.cmp, label %strcmp.false, label %strcmp.loop_null_cmp strcmp.false: ; preds = %strcmp.done, %strcmp.loop, %entry %4 = load i1, ptr %strcmp.result, align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %strcmp.result) %5 = zext i1 %4 to i64 call void @llvm.lifetime.end.p0(i64 -1, ptr %comm) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 %5, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 1, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") ret i64 0 strcmp.done: ; preds = %strcmp.loop1, %strcmp.loop_null_cmp2, %strcmp.loop_null_cmp store i1 false, ptr %strcmp.result, align 1 br label %strcmp.false strcmp.loop: ; preds = %strcmp.loop_null_cmp %6 = getelementptr i8, ptr %comm, i32 1 %7 = load i8, ptr %6, align 1 %8 = load i8, ptr getelementptr (i8, ptr @sshd, i32 1), align 1 %strcmp.cmp3 = icmp ne i8 %7, %8 br i1 %strcmp.cmp3, label %strcmp.false, label %strcmp.loop_null_cmp2 strcmp.loop_null_cmp: ; preds = %entry %strcmp.cmp_null = icmp eq i8 %2, 0 br i1 %strcmp.cmp_null, label %strcmp.done, label %strcmp.loop strcmp.loop1: ; preds = %strcmp.loop_null_cmp2 br label %strcmp.done strcmp.loop_null_cmp2: ; preds = %strcmp.loop %strcmp.cmp_null4 = icmp eq i8 %7, 0 br i1 %strcmp.cmp_null4, label %strcmp.done, label %strcmp.loop1 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!47} !llvm.module.flags = !{!49} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !44, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !48) !48 = !{!0, !20, !34} !49 = !{i32 2, !"Debug Info Version", i32 3} !50 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !47, retainedNodes: !55) !51 = !DISubroutineType(types: !52) !52 = !{!18, !53} !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64) !54 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !50, file: !2, type: !53) bpftrace-0.23.2/tests/codegen/llvm/struct_char_1.ll000066400000000000000000000147501477746507000222260ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i8, align 1 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 1, ptr %6) %7 = load i8, ptr %"struct Foo.x", align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %8 = sext i8 %7 to i64 store i64 %8, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_char_2.ll000066400000000000000000000147501477746507000222270ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i8, align 1 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 1, ptr %6) %7 = load i8, ptr %"struct Foo.x", align 1 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %8 = sext i8 %7 to i64 store i64 %8, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_integer_ptr_1.ll000066400000000000000000000153631477746507000236340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %deref = alloca i32, align 4 %"struct Foo.x" = alloca i64, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 8, ptr %6) %7 = load i64, ptr %"struct Foo.x", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %deref) %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %deref, i32 4, i64 %7) %8 = load i32, ptr %deref, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %deref) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %9 = sext i32 %8 to i64 store i64 %9, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_integer_ptr_2.ll000066400000000000000000000153631477746507000236350ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %deref = alloca i32, align 4 %"struct Foo.x" = alloca i64, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 8, ptr %6) %7 = load i64, ptr %"struct Foo.x", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %deref) %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %deref, i32 4, i64 %7) %8 = load i32, ptr %deref, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %deref) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %9 = sext i32 %8 to i64 store i64 %9, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_integers_1.ll000066400000000000000000000147531477746507000231340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 4, ptr %6) %7 = load i32, ptr %"struct Foo.x", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %8 = sext i32 %7 to i64 store i64 %8, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_integers_2.ll000066400000000000000000000147531477746507000231350ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 4, ptr %6) %7 = load i32, ptr %"struct Foo.x", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %8 = sext i32 %7 to i64 store i64 %8, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_long_1.ll000066400000000000000000000147211477746507000222460ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i64, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 8, ptr %6) %7 = load i64, ptr %"struct Foo.x", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %7, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_long_2.ll000066400000000000000000000147211477746507000222470ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i64, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 8, ptr %6) %7 = load i64, ptr %"struct Foo.x", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %7, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_nested_struct_anon_1.ll000066400000000000000000000153561477746507000252150ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo::(unnamed at definitions.h:2:14).x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 %7 = call ptr @llvm.preserve.static.offset(ptr %6) %8 = getelementptr i8, ptr %7, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo::(unnamed at definitions.h:2:14).x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo::(unnamed at definitions.h:2:14).x", i32 4, ptr %8) %9 = load i32, ptr %"struct Foo::(unnamed at definitions.h:2:14).x", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo::(unnamed at definitions.h:2:14).x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %10 = sext i32 %9 to i64 store i64 %10, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_nested_struct_anon_2.ll000066400000000000000000000153561477746507000252160ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo::(unnamed at definitions.h:2:14).x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 %7 = call ptr @llvm.preserve.static.offset(ptr %6) %8 = getelementptr i8, ptr %7, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo::(unnamed at definitions.h:2:14).x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo::(unnamed at definitions.h:2:14).x", i32 4, ptr %8) %9 = load i32, ptr %"struct Foo::(unnamed at definitions.h:2:14).x", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo::(unnamed at definitions.h:2:14).x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %10 = sext i32 %9 to i64 store i64 %10, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_nested_struct_named_1.ll000066400000000000000000000151111477746507000253330ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Bar.x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 %7 = call ptr @llvm.preserve.static.offset(ptr %6) %8 = getelementptr i8, ptr %7, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Bar.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Bar.x", i32 4, ptr %8) %9 = load i32, ptr %"struct Bar.x", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Bar.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %10 = sext i32 %9 to i64 store i64 %10, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_nested_struct_named_2.ll000066400000000000000000000151111477746507000253340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Bar.x" = alloca i32, align 4 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 %7 = call ptr @llvm.preserve.static.offset(ptr %6) %8 = getelementptr i8, ptr %7, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Bar.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Bar.x", i32 4, ptr %8) %9 = load i32, ptr %"struct Bar.x", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Bar.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %10 = sext i32 %9 to i64 store i64 %10, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_nested_struct_ptr_named_1.ll000066400000000000000000000156521477746507000262320ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Bar.x" = alloca i32, align 4 %"struct Foo.bar" = alloca i64, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.bar") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.bar", i32 8, ptr %6) %7 = load i64, ptr %"struct Foo.bar", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.bar") %8 = inttoptr i64 %7 to ptr %9 = call ptr @llvm.preserve.static.offset(ptr %8) %10 = getelementptr i8, ptr %9, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Bar.x") %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Bar.x", i32 4, ptr %10) %11 = load i32, ptr %"struct Bar.x", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Bar.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %12 = sext i32 %11 to i64 store i64 %12, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_nested_struct_ptr_named_2.ll000066400000000000000000000156521477746507000262330ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Bar.x" = alloca i32, align 4 %"struct Foo.bar" = alloca i64, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.bar") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.bar", i32 8, ptr %6) %7 = load i64, ptr %"struct Foo.bar", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.bar") %8 = inttoptr i64 %7 to ptr %9 = call ptr @llvm.preserve.static.offset(ptr %8) %10 = getelementptr i8, ptr %9, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Bar.x") %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Bar.x", i32 4, ptr %10) %11 = load i32, ptr %"struct Bar.x", align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Bar.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %12 = sext i32 %11 to i64 store i64 %12, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_save_1.ll000066400000000000000000000143701477746507000222450ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_foo = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@foo_val" = alloca [12 x i8], align 1 %"@foo_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_key") store i64 0, ptr %"@foo_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_val") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"@foo_val", i32 12, i64 %arg0) %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_foo, ptr %"@foo_key", ptr %"@foo_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_foo", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 96, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 12, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/struct_save_2.ll000066400000000000000000000143701477746507000222460ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_foo = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@foo_val" = alloca [12 x i8], align 1 %"@foo_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_key") store i64 0, ptr %"@foo_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_val") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"@foo_val", i32 12, i64 %arg0) %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_foo, ptr %"@foo_key", ptr %"@foo_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_foo", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 96, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 12, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/struct_save_nested.ll000066400000000000000000000261661477746507000233750ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct map_t.2" = type { ptr, ptr } %"struct map_t.3" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_bar = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_foo = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @AT_x = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 @ringbuf = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !35 @event_loss_counter = dso_local global %"struct map_t.3" zeroinitializer, section ".maps", !dbg !49 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !64 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %lookup_elem_val8 = alloca [16 x i8], align 1 %"@foo_key3" = alloca i64, align 8 %"@bar_key" = alloca i64, align 8 %lookup_elem_val = alloca [16 x i8], align 1 %"@foo_key1" = alloca i64, align 8 %"@foo_val" = alloca [16 x i8], align 1 %"@foo_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_key") store i64 0, ptr %"@foo_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_val") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"@foo_val", i32 16, i64 %arg0) %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_foo, ptr %"@foo_key", ptr %"@foo_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_key1") store i64 0, ptr %"@foo_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_foo, ptr %"@foo_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.memcpy.p0.p0.i64(ptr align 1 %lookup_elem_val, ptr align 1 %lookup_elem, i64 16, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.memset.p0.i64(ptr align 1 %lookup_elem_val, i8 0, i64 16, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_key1") %3 = getelementptr [16 x i8], ptr %lookup_elem_val, i32 0, i64 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@bar_key") store i64 0, ptr %"@bar_key", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_bar, ptr %"@bar_key", ptr %3, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@bar_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_key3") store i64 0, ptr %"@foo_key3", align 8 %lookup_elem4 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_foo, ptr %"@foo_key3") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val8) %map_lookup_cond9 = icmp ne ptr %lookup_elem4, null br i1 %map_lookup_cond9, label %lookup_success5, label %lookup_failure6 lookup_success5: ; preds = %lookup_merge call void @llvm.memcpy.p0.p0.i64(ptr align 1 %lookup_elem_val8, ptr align 1 %lookup_elem4, i64 16, i1 false) br label %lookup_merge7 lookup_failure6: ; preds = %lookup_merge call void @llvm.memset.p0.i64(ptr align 1 %lookup_elem_val8, i8 0, i64 16, i1 false) br label %lookup_merge7 lookup_merge7: ; preds = %lookup_failure6, %lookup_success5 call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_key3") %4 = getelementptr [16 x i8], ptr %lookup_elem_val8, i32 0, i64 4 %5 = getelementptr [8 x i8], ptr %4, i32 0, i64 0 %6 = load volatile i32, ptr %5, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val8) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %7 = sext i32 %6 to i64 store i64 %7, ptr %"@x_val", align 8 %update_elem10 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #4 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!61} !llvm.module.flags = !{!63} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_bar", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 64, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 8, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "AT_foo", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !24) !24 = !{!5, !11, !12, !25} !25 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !26, size: 64, offset: 192) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 16, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!5, !11, !12, !34} !34 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !38) !38 = !{!39, !44} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 27, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !45, size: 64, offset: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 262144, lowerBound: 0) !49 = !DIGlobalVariableExpression(var: !50, expr: !DIExpression()) !50 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !51, isLocal: false, isDefinition: true) !51 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !52) !52 = !{!53, !11, !58, !34} !53 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !54, size: 64) !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !56) !56 = !{!57} !57 = !DISubrange(count: 2, lowerBound: 0) !58 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !59, size: 64, offset: 128) !59 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !60, size: 64) !60 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !61 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !62) !62 = !{!0, !21, !30, !35, !49} !63 = !{i32 2, !"Debug Info Version", i32 3} !64 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !65, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !61, retainedNodes: !68) !65 = !DISubroutineType(types: !66) !66 = !{!14, !67} !67 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !68 = !{!69} !69 = !DILocalVariable(name: "ctx", arg: 1, scope: !64, file: !2, type: !67) bpftrace-0.23.2/tests/codegen/llvm/struct_save_string.ll000066400000000000000000000210021477746507000234010ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_foo = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_str = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !23 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !37 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !53 { entry: %"@str_key" = alloca i64, align 8 %lookup_elem_val = alloca [32 x i8], align 1 %"@foo_key1" = alloca i64, align 8 %"@foo_val" = alloca [32 x i8], align 1 %"@foo_key" = alloca i64, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_key") store i64 0, ptr %"@foo_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_val") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"@foo_val", i32 32, i64 %arg0) %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_foo, ptr %"@foo_key", ptr %"@foo_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@foo_key1") store i64 0, ptr %"@foo_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_foo, ptr %"@foo_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry call void @llvm.memcpy.p0.p0.i64(ptr align 1 %lookup_elem_val, ptr align 1 %lookup_elem, i64 32, i1 false) br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.memset.p0.i64(ptr align 1 %lookup_elem_val, i8 0, i64 32, i1 false) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %"@foo_key1") %3 = getelementptr [32 x i8], ptr %lookup_elem_val, i32 0, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@str_key") store i64 0, ptr %"@str_key", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_str, ptr %"@str_key", ptr %3, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@str_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #4 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!50} !llvm.module.flags = !{!52} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_foo", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 256, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 32, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "AT_str", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !23 = !DIGlobalVariableExpression(var: !24, expr: !DIExpression()) !24 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !25, isLocal: false, isDefinition: true) !25 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !26) !26 = !{!27, !32} !27 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !28, size: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 27, lowerBound: 0) !32 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !33, size: 64, offset: 64) !33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !34, size: 64) !34 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !35) !35 = !{!36} !36 = !DISubrange(count: 262144, lowerBound: 0) !37 = !DIGlobalVariableExpression(var: !38, expr: !DIExpression()) !38 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !39, isLocal: false, isDefinition: true) !39 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !40) !40 = !{!41, !11, !46, !49} !41 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !42, size: 64) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !44) !44 = !{!45} !45 = !DISubrange(count: 2, lowerBound: 0) !46 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !47, size: 64, offset: 128) !47 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !48, size: 64) !48 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !49 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !50 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !51) !51 = !{!0, !21, !23, !37} !52 = !{i32 2, !"Debug Info Version", i32 3} !53 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !54, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !50, retainedNodes: !57) !54 = !DISubroutineType(types: !55) !55 = !{!14, !56} !56 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !57 = !{!58} !58 = !DILocalVariable(name: "ctx", arg: 1, scope: !53, file: !2, type: !56) bpftrace-0.23.2/tests/codegen/llvm/struct_short_1.ll000066400000000000000000000147531477746507000224530ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i16, align 2 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 2, ptr %6) %7 = load i16, ptr %"struct Foo.x", align 2 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %8 = sext i16 %7 to i64 store i64 %8, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_short_2.ll000066400000000000000000000147531477746507000224540ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"struct Foo.x" = alloca i16, align 2 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.x") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.x", i32 2, ptr %6) %7 = load i16, ptr %"struct Foo.x", align 2 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.x") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %8 = sext i16 %7 to i64 store i64 %8, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/struct_string_array_1.ll000066400000000000000000000151511477746507000240110ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_mystr = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@mystr_key" = alloca i64, align 8 %"struct Foo.str" = alloca [32 x i8], align 1 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.str") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.str", i32 32, ptr %6) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@mystr_key") store i64 0, ptr %"@mystr_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_mystr, ptr %"@mystr_key", ptr %"struct Foo.str", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@mystr_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.str") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_mystr", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 256, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 32, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/struct_string_array_2.ll000066400000000000000000000151511477746507000240120ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_mystr = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@mystr_key" = alloca i64, align 8 %"struct Foo.str" = alloca [32 x i8], align 1 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %3 = load i64, ptr %"$foo", align 8 %4 = inttoptr i64 %3 to ptr %5 = call ptr @llvm.preserve.static.offset(ptr %4) %6 = getelementptr i8, ptr %5, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.str") %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.str", i32 32, ptr %6) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@mystr_key") store i64 0, ptr %"@mystr_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_mystr, ptr %"@mystr_key", ptr %"struct Foo.str", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@mystr_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.str") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_mystr", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 256, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 32, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/struct_string_ptr.ll000066400000000000000000000175671477746507000232750ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_mystr = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !48 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !50 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !57 { entry: %"@mystr_key" = alloca i64, align 8 %"struct Foo.str" = alloca i64, align 8 %"$foo" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$foo") store i64 0, ptr %"$foo", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 store i64 %arg0, ptr %"$foo", align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %3 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %3 %4 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %4, i32 1024, ptr null) %5 = load i64, ptr %"$foo", align 8 %6 = inttoptr i64 %5 to ptr %7 = call ptr @llvm.preserve.static.offset(ptr %6) %8 = getelementptr i8, ptr %7, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %"struct Foo.str") %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %"struct Foo.str", i32 8, ptr %8) %9 = load i64, ptr %"struct Foo.str", align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %"struct Foo.str") %probe_read_kernel_str = call i64 inttoptr (i64 115 to ptr)(ptr %4, i32 1024, i64 %9) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@mystr_key") store i64 0, ptr %"@mystr_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_mystr, ptr %"@mystr_key", ptr %4, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@mystr_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!54} !llvm.module.flags = !{!56} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_mystr", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 8192, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 1024, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = !DIGlobalVariableExpression(var: !49, expr: !DIExpression()) !49 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !50 = !DIGlobalVariableExpression(var: !51, expr: !DIExpression()) !51 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !52, isLocal: false, isDefinition: true) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !53, size: 8192, elements: !9) !53 = !DICompositeType(tag: DW_TAG_array_type, baseType: !17, size: 8192, elements: !9) !54 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !55) !55 = !{!0, !21, !35, !48, !50} !56 = !{i32 2, !"Debug Info Version", i32 3} !57 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !58, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !54, retainedNodes: !61) !58 = !DISubroutineType(types: !59) !59 = !{!14, !60} !60 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !57, file: !2, type: !60) bpftrace-0.23.2/tests/codegen/llvm/subprog_arguments.ll000066400000000000000000000100311477746507000232170ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define internal i64 @add(ptr %0, i64 %1, i64 %2) { entry: %"$b" = alloca i64, align 8 %"$a" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$a") store i64 %1, ptr %"$a", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$b") store i64 %2, ptr %"$b", align 8 %3 = load i64, ptr %"$a", align 8 %4 = load i64, ptr %"$b", align 8 %5 = add i64 %3, %4 ret i64 %5 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} bpftrace-0.23.2/tests/codegen/llvm/sum_cast.ll000066400000000000000000000221471477746507000213020ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !46 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %"@x_key1" = alloca i64, align 8 %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 %2 = add i64 %1, 2 store i64 %2, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 2, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key1") store i64 0, ptr %"@x_key1", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %lookup_success2, %lookup_merge %3 = load i32, ptr @num_cpus, align 4 %4 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %4, %3 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %5 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %"@x_key1", i32 %5) %map_lookup_cond4 = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond4, label %lookup_success2, label %lookup_failure3 while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %6 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key1") store i64 %6, ptr %"$res", align 8 ret i64 0 lookup_success2: ; preds = %while_body %7 = load i64, ptr %val_1, align 8 %8 = load i64, ptr %lookup_percpu_elem, align 8 %9 = add i64 %8, %7 store i64 %9, ptr %val_1, align 8 %10 = load i32, ptr %i, align 4 %11 = add i32 %10, 1 store i32 %11, ptr %i, align 4 br label %while_cond lookup_failure3: ; preds = %while_body %12 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %12, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure error_success: ; preds = %lookup_failure3 br label %while_end error_failure: ; preds = %lookup_failure3 %13 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 1, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !11, !43, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !44, size: 64, offset: 128) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !46 = !DIGlobalVariableExpression(var: !47, expr: !DIExpression()) !47 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !20, !34, !46} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !56) !52 = !DISubroutineType(types: !53) !53 = !{!18, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !55, size: 64) !55 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !56 = !{!57} !57 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/sum_cast_loop.ll000066400000000000000000000255611477746507000223360ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %int64_sum_t__tuple_t = type { i64, i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !20 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !34 @num_cpus = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !51 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !56 { entry: %initial_value = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@x_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 1, ptr %"@x_key", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_x, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 %2 = add i64 %1, 2 store i64 %2, ptr %lookup_elem, align 8 br label %lookup_merge lookup_failure: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %initial_value) store i64 2, ptr %initial_value, align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %initial_value, i64 1) call void @llvm.lifetime.end.p0(i64 -1, ptr %initial_value) br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") %for_each_map_elem = call i64 inttoptr (i64 164 to ptr)(ptr @AT_x, ptr @map_for_each_cb, ptr null, i64 0) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 define internal i64 @map_for_each_cb(ptr %0, ptr %1, ptr %2, ptr %3) section ".text" !dbg !63 { %"$res" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$res") store i64 0, ptr %"$res", align 8 %"$kv" = alloca %int64_sum_t__tuple_t, align 8 %val_2 = alloca i64, align 8 %val_1 = alloca i64, align 8 %i = alloca i32, align 4 %key = load i64, ptr %1, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %i) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.start.p0(i64 -1, ptr %val_2) store i32 0, ptr %i, align 4 store i64 0, ptr %val_1, align 8 store i64 0, ptr %val_2, align 8 br label %while_cond while_cond: ; preds = %lookup_success, %4 %5 = load i32, ptr @num_cpus, align 4 %6 = load i32, ptr %i, align 4 %num_cpu.cmp = icmp ult i32 %6, %5 br i1 %num_cpu.cmp, label %while_body, label %while_end while_body: ; preds = %while_cond %7 = load i32, ptr %i, align 4 %lookup_percpu_elem = call ptr inttoptr (i64 195 to ptr)(ptr @AT_x, ptr %1, i32 %7) %map_lookup_cond = icmp ne ptr %lookup_percpu_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure while_end: ; preds = %error_failure, %error_success, %while_cond call void @llvm.lifetime.end.p0(i64 -1, ptr %i) %8 = load i64, ptr %val_1, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %val_1) call void @llvm.lifetime.end.p0(i64 -1, ptr %val_2) call void @llvm.lifetime.start.p0(i64 -1, ptr %"$kv") call void @llvm.memset.p0.i64(ptr align 1 %"$kv", i8 0, i64 16, i1 false) %9 = getelementptr %int64_sum_t__tuple_t, ptr %"$kv", i32 0, i32 0 store i64 %key, ptr %9, align 8 %10 = getelementptr %int64_sum_t__tuple_t, ptr %"$kv", i32 0, i32 1 store i64 %8, ptr %10, align 8 %11 = getelementptr %int64_sum_t__tuple_t, ptr %"$kv", i32 0, i32 1 %12 = load i64, ptr %11, align 8 store i64 %12, ptr %"$res", align 8 ret i64 0 lookup_success: ; preds = %while_body %13 = load i64, ptr %val_1, align 8 %14 = load i64, ptr %lookup_percpu_elem, align 8 %15 = add i64 %14, %13 store i64 %15, ptr %val_1, align 8 %16 = load i32, ptr %i, align 4 %17 = add i32 %16, 1 store i32 %17, ptr %i, align 4 br label %while_cond lookup_failure: ; preds = %while_body %18 = load i32, ptr %i, align 4 %error_lookup_cond = icmp eq i32 %18, 0 br i1 %error_lookup_cond, label %error_success, label %error_failure error_success: ; preds = %lookup_failure br label %while_end error_failure: ; preds = %lookup_failure %19 = load i32, ptr %i, align 4 br label %while_end } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!53} !llvm.module.flags = !{!55} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !19} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 160, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 5, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !19 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !17, size: 64, offset: 192) !20 = !DIGlobalVariableExpression(var: !21, expr: !DIExpression()) !21 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !22, isLocal: false, isDefinition: true) !22 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !23) !23 = !{!24, !29} !24 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !25, size: 64) !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !27) !27 = !{!28} !28 = !DISubrange(count: 27, lowerBound: 0) !29 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !30, size: 64, offset: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 262144, lowerBound: 0) !34 = !DIGlobalVariableExpression(var: !35, expr: !DIExpression()) !35 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !36, isLocal: false, isDefinition: true) !36 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !37) !37 = !{!38, !43, !48, !19} !38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !39, size: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 2, lowerBound: 0) !43 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !44, size: 64, offset: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 1, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !49, size: 64, offset: 128) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !51 = !DIGlobalVariableExpression(var: !52, expr: !DIExpression()) !52 = distinct !DIGlobalVariable(name: "num_cpus", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !53 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !54) !54 = !{!0, !20, !34, !51} !55 = !{i32 2, !"Debug Info Version", i32 3} !56 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !57, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !53, retainedNodes: !61) !57 = !DISubroutineType(types: !58) !58 = !{!18, !59} !59 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !60, size: 64) !60 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !61 = !{!62} !62 = !DILocalVariable(name: "ctx", arg: 1, scope: !56, file: !2, type: !59) !63 = distinct !DISubprogram(name: "map_for_each_cb", linkageName: "map_for_each_cb", scope: !2, file: !2, type: !64, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !53, retainedNodes: !66) !64 = !DISubroutineType(types: !65) !65 = !{!18, !59, !59, !59, !59} !66 = !{!67, !68, !69, !70} !67 = !DILocalVariable(name: "map", arg: 1, scope: !63, file: !2, type: !59) !68 = !DILocalVariable(name: "key", arg: 2, scope: !63, file: !2, type: !59) !69 = !DILocalVariable(name: "value", arg: 3, scope: !63, file: !2, type: !59) !70 = !DILocalVariable(name: "ctx", arg: 4, scope: !63, file: !2, type: !59) bpftrace-0.23.2/tests/codegen/llvm/ternary_buf.ll000066400000000000000000000200221477746507000217720ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %buffer_2_t = type <{ i32, [2 x i8] }> %buffer_3_t = type <{ i32, [3 x i8] }> @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @get_str_buf = dso_local externally_initialized global [1 x [2 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !38 @hi = global [3 x i8] c"hi\00" @bye = global [4 x i8] c"bye\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !49 { entry: %"$x" = alloca [7 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") call void @llvm.memset.p0.i64(ptr align 1 %"$x", i8 0, i64 7, i1 false) %1 = alloca [7 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %1) call void @llvm.memset.p0.i64(ptr align 1 %1, i8 0, i64 7, i1 false) %get_ns = call i64 inttoptr (i64 125 to ptr)() %true_cond = icmp ne i64 %get_ns, 0 br i1 %true_cond, label %left, label %right left: ; preds = %entry %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %2 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %2 %3 = getelementptr [1 x [2 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %4 = getelementptr %buffer_2_t, ptr %3, i32 0, i32 0 store i32 2, ptr %4, align 4 %5 = getelementptr %buffer_2_t, ptr %3, i32 0, i32 1 call void @llvm.memset.p0.i64(ptr align 1 %5, i8 0, i64 2, i1 false) %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %5, i32 2, ptr @hi) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %1, ptr align 1 %3, i64 7, i1 false) br label %done right: ; preds = %entry %get_cpu_id1 = call i64 inttoptr (i64 8 to ptr)() %6 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded2 = and i64 %get_cpu_id1, %6 %7 = getelementptr [1 x [2 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded2, i64 1, i64 0 %8 = getelementptr %buffer_3_t, ptr %7, i32 0, i32 0 store i32 3, ptr %8, align 4 %9 = getelementptr %buffer_3_t, ptr %7, i32 0, i32 1 call void @llvm.memset.p0.i64(ptr align 1 %9, i8 0, i64 3, i1 false) %probe_read_kernel3 = call i64 inttoptr (i64 113 to ptr)(ptr %9, i32 3, ptr @bye) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %1, ptr align 1 %7, i64 7, i1 false) br label %done done: ; preds = %right, %left call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$x", ptr align 1 %1, i64 7, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %1) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 16384, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 16384, elements: !23) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 8192, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 1024, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/ternary_int.ll000066400000000000000000000140061477746507000220150ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %3 = icmp ult i64 %2, 10000 %true_cond = icmp ne i1 %3, false br i1 %true_cond, label %left, label %right left: ; preds = %entry br label %done right: ; preds = %entry br label %done done: ; preds = %right, %left %result = phi i64 [ 1, %left ], [ 2, %right ] call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 %result, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/ternary_none.ll000066400000000000000000000201531477746507000221620ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %exit_t = type <{ i64, i8 }> %printf_t = type { i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %key5 = alloca i32, align 4 %exit = alloca %exit_t, align 8 %key = alloca i32, align 4 %printf_args = alloca %printf_t, align 8 %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %1 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %1 to i32 %2 = zext i32 %pid to i64 %3 = icmp ult i64 %2, 10000 %true_cond = icmp ne i1 %3, false br i1 %true_cond, label %left, label %right left: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %printf_args) call void @llvm.memset.p0.i64(ptr align 1 %printf_args, i8 0, i64 8, i1 false) %4 = getelementptr %printf_t, ptr %printf_args, i32 0, i32 0 store i64 0, ptr %4, align 8 %ringbuf_output = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %printf_args, i64 8, i64 0) %ringbuf_loss = icmp slt i64 %ringbuf_output, 0 br i1 %ringbuf_loss, label %event_loss_counter, label %counter_merge right: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %exit) %5 = getelementptr %exit_t, ptr %exit, i64 0, i32 0 store i64 30000, ptr %5, align 8 %6 = getelementptr %exit_t, ptr %exit, i64 0, i32 1 store i8 0, ptr %6, align 1 %ringbuf_output1 = call i64 inttoptr (i64 130 to ptr)(ptr @ringbuf, ptr %exit, i64 9, i64 0) %ringbuf_loss4 = icmp slt i64 %ringbuf_output1, 0 br i1 %ringbuf_loss4, label %event_loss_counter2, label %counter_merge3 done: ; preds = %deadcode, %counter_merge ret i64 0 event_loss_counter: ; preds = %left call void @llvm.lifetime.start.p0(i64 -1, ptr %key) store i32 0, ptr %key, align 4 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure counter_merge: ; preds = %lookup_merge, %left call void @llvm.lifetime.end.p0(i64 -1, ptr %printf_args) br label %done lookup_success: ; preds = %event_loss_counter %7 = atomicrmw add ptr %lookup_elem, i64 1 seq_cst, align 8 br label %lookup_merge lookup_failure: ; preds = %event_loss_counter br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success call void @llvm.lifetime.end.p0(i64 -1, ptr %key) br label %counter_merge event_loss_counter2: ; preds = %right call void @llvm.lifetime.start.p0(i64 -1, ptr %key5) store i32 0, ptr %key5, align 4 %lookup_elem6 = call ptr inttoptr (i64 1 to ptr)(ptr @event_loss_counter, ptr %key5) %map_lookup_cond10 = icmp ne ptr %lookup_elem6, null br i1 %map_lookup_cond10, label %lookup_success7, label %lookup_failure8 counter_merge3: ; preds = %lookup_merge9, %right call void @llvm.lifetime.end.p0(i64 -1, ptr %exit) ret i64 0 lookup_success7: ; preds = %event_loss_counter2 %8 = atomicrmw add ptr %lookup_elem6, i64 1 seq_cst, align 8 br label %lookup_merge9 lookup_failure8: ; preds = %event_loss_counter2 br label %lookup_merge9 lookup_merge9: ; preds = %lookup_failure8, %lookup_success7 call void @llvm.lifetime.end.p0(i64 -1, ptr %key5) br label %counter_merge3 deadcode: ; No predecessors! br label %done } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/ternary_str.ll000066400000000000000000000176261477746507000220460ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !48 @get_str_buf = dso_local externally_initialized global [1 x [1 x [1024 x i8]]] zeroinitializer, section ".data.get_str_buf", !dbg !50 @lo = global [3 x i8] c"lo\00" @hi = global [3 x i8] c"hi\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !60 { entry: %"@x_key" = alloca i64, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [1024 x i8]]], ptr @get_str_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %2, i32 1024, ptr null) %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %3 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %3 to i32 %4 = zext i32 %pid to i64 %5 = icmp ult i64 %4, 10000 %true_cond = icmp ne i1 %5, false br i1 %true_cond, label %left, label %right left: ; preds = %entry call void @llvm.memcpy.p0.p0.i64(ptr align 1 %2, ptr align 1 @lo, i64 3, i1 false) br label %done right: ; preds = %entry call void @llvm.memcpy.p0.p0.i64(ptr align 1 %2, ptr align 1 @hi, i64 3, i1 false) br label %done done: ; preds = %right, %left call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %2, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!57} !llvm.module.flags = !{!59} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 24, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 3, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = !DIGlobalVariableExpression(var: !49, expr: !DIExpression()) !49 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !14, isLocal: false, isDefinition: true) !50 = !DIGlobalVariableExpression(var: !51, expr: !DIExpression()) !51 = distinct !DIGlobalVariable(name: "get_str_buf", linkageName: "global", scope: !2, file: !2, type: !52, isLocal: false, isDefinition: true) !52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !53, size: 8192, elements: !9) !53 = !DICompositeType(tag: DW_TAG_array_type, baseType: !54, size: 8192, elements: !9) !54 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 8192, elements: !55) !55 = !{!56} !56 = !DISubrange(count: 1024, lowerBound: 0) !57 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !58) !58 = !{!0, !21, !35, !48, !50} !59 = !{i32 2, !"Debug Info Version", i32 3} !60 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !57, retainedNodes: !64) !61 = !DISubroutineType(types: !62) !62 = !{!14, !63} !63 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !64 = !{!65} !65 = !DILocalVariable(name: "ctx", arg: 1, scope: !60, file: !2, type: !63) bpftrace-0.23.2/tests/codegen/llvm/ternary_tuple.ll000066400000000000000000000174331477746507000223630ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"string[16]_int64__tuple_t" = type { [16 x i8], i64 } %"string[3]_int64__tuple_t" = type { [3 x i8], i64 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @hi = global [3 x i8] c"hi\00" @extralongstring = global [16 x i8] c"extralongstring\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"$x" = alloca %"string[16]_int64__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") call void @llvm.memset.p0.i64(ptr align 1 %"$x", i8 0, i64 24, i1 false) %tuple1 = alloca %"string[16]_int64__tuple_t", align 8 %tuple = alloca %"string[3]_int64__tuple_t", align 8 %1 = alloca %"string[16]_int64__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %1) call void @llvm.memset.p0.i64(ptr align 1 %1, i8 0, i64 24, i1 false) %get_ns = call i64 inttoptr (i64 125 to ptr)() %true_cond = icmp ne i64 %get_ns, 0 br i1 %true_cond, label %left, label %right left: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 16, i1 false) %2 = getelementptr %"string[3]_int64__tuple_t", ptr %tuple, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %2, ptr align 1 @hi, i64 3, i1 false) %3 = getelementptr %"string[3]_int64__tuple_t", ptr %tuple, i32 0, i32 1 store i64 1, ptr %3, align 8 %4 = getelementptr [16 x i8], ptr %tuple, i64 0, i64 0 %5 = getelementptr %"string[16]_int64__tuple_t", ptr %1, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %5, ptr align 1 %4, i64 3, i1 false) %6 = getelementptr [16 x i8], ptr %tuple, i64 0, i64 8 %7 = getelementptr %"string[16]_int64__tuple_t", ptr %1, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %7, ptr align 1 %6, i64 8, i1 false) br label %done right: ; preds = %entry call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple1) call void @llvm.memset.p0.i64(ptr align 1 %tuple1, i8 0, i64 24, i1 false) %8 = getelementptr %"string[16]_int64__tuple_t", ptr %tuple1, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %8, ptr align 1 @extralongstring, i64 16, i1 false) %9 = getelementptr %"string[16]_int64__tuple_t", ptr %tuple1, i32 0, i32 1 store i64 2, ptr %9, align 8 %10 = getelementptr [24 x i8], ptr %tuple1, i64 0, i64 0 %11 = getelementptr %"string[16]_int64__tuple_t", ptr %1, i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %11, ptr align 1 %10, i64 16, i1 false) %12 = getelementptr [24 x i8], ptr %tuple1, i64 0, i64 16 %13 = getelementptr %"string[16]_int64__tuple_t", ptr %1, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %13, ptr align 1 %12, i64 8, i1 false) br label %done done: ; preds = %right, %left call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple1) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$x", ptr align 1 %1, i64 24, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %1) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/tuple.ll000066400000000000000000000164021477746507000206120ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"int64_int64_string[4]__tuple_t" = type { i64, i64, [4 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_t = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 @str = global [4 x i8] c"str\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !56 { entry: %"@t_key" = alloca i64, align 8 %tuple = alloca %"int64_int64_string[4]__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 24, i1 false) %1 = getelementptr %"int64_int64_string[4]__tuple_t", ptr %tuple, i32 0, i32 0 store i64 1, ptr %1, align 8 %2 = getelementptr %"int64_int64_string[4]__tuple_t", ptr %tuple, i32 0, i32 1 store i64 2, ptr %2, align 8 %3 = getelementptr %"int64_int64_string[4]__tuple_t", ptr %tuple, i32 0, i32 2 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %3, ptr align 1 @str, i64 4, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@t_key") store i64 0, ptr %"@t_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_t, ptr %"@t_key", ptr %tuple, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@t_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!53} !llvm.module.flags = !{!55} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_t", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 192, elements: !18) !18 = !{!19, !20, !21} !19 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !14, size: 64) !20 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !14, size: 64, offset: 64) !21 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !22, size: 32, offset: 128) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !23, size: 32, elements: !24) !23 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !24 = !{!25} !25 = !DISubrange(count: 4, lowerBound: 0) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !52} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !52 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !53 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !54) !54 = !{!0, !26, !40} !55 = !{i32 2, !"Debug Info Version", i32 3} !56 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !57, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !53, retainedNodes: !60) !57 = !DISubroutineType(types: !58) !58 = !{!14, !59} !59 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64) !60 = !{!61} !61 = !DILocalVariable(name: "ctx", arg: 1, scope: !56, file: !2, type: !59) bpftrace-0.23.2/tests/codegen/llvm/tuple_array_struct.ll000066400000000000000000000170361477746507000234200ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"struct Foo_int32[4]__tuple_t" = type { [8 x i8], [4 x i32] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_t = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !29 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !43 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !58 { entry: %"@t_key" = alloca i64, align 8 %tuple = alloca %"struct Foo_int32[4]__tuple_t", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = call ptr @llvm.preserve.static.offset(ptr %0) %4 = getelementptr i64, ptr %3, i64 13 %arg1 = load volatile i64, ptr %4, align 8 %5 = inttoptr i64 %arg1 to ptr %6 = call ptr @llvm.preserve.static.offset(ptr %5) %7 = getelementptr i8, ptr %6, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 24, i1 false) %8 = getelementptr %"struct Foo_int32[4]__tuple_t", ptr %tuple, i32 0, i32 0 %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %8, i32 8, i64 %arg0) %9 = getelementptr %"struct Foo_int32[4]__tuple_t", ptr %tuple, i32 0, i32 1 %probe_read_kernel1 = call i64 inttoptr (i64 113 to ptr)(ptr %9, i32 16, ptr %7) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@t_key") store i64 0, ptr %"@t_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_t, ptr %"@t_key", ptr %tuple, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@t_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } !llvm.dbg.cu = !{!55} !llvm.module.flags = !{!57} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_t", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 192, elements: !18) !18 = !{!19, !24} !19 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !20, size: 64) !20 = !DICompositeType(tag: DW_TAG_array_type, baseType: !21, size: 64, elements: !22) !21 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !22 = !{!23} !23 = !DISubrange(count: 8, lowerBound: 0) !24 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !25, size: 128, offset: 64) !25 = !DICompositeType(tag: DW_TAG_array_type, baseType: !26, size: 128, elements: !27) !26 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !27 = !{!28} !28 = !DISubrange(count: 4, lowerBound: 0) !29 = !DIGlobalVariableExpression(var: !30, expr: !DIExpression()) !30 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !31, isLocal: false, isDefinition: true) !31 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !32) !32 = !{!33, !38} !33 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !34, size: 64) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !36) !36 = !{!37} !37 = !DISubrange(count: 27, lowerBound: 0) !38 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !39, size: 64, offset: 64) !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !41) !41 = !{!42} !42 = !DISubrange(count: 262144, lowerBound: 0) !43 = !DIGlobalVariableExpression(var: !44, expr: !DIExpression()) !44 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !45, isLocal: false, isDefinition: true) !45 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !46) !46 = !{!47, !11, !52, !54} !47 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !48, size: 64) !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !50) !50 = !{!51} !51 = !DISubrange(count: 2, lowerBound: 0) !52 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !53, size: 64, offset: 128) !53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64) !54 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !55 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !56) !56 = !{!0, !29, !43} !57 = !{i32 2, !"Debug Info Version", i32 3} !58 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !59, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !55, retainedNodes: !62) !59 = !DISubroutineType(types: !60) !60 = !{!14, !61} !61 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) !62 = !{!63} !63 = !DILocalVariable(name: "ctx", arg: 1, scope: !58, file: !2, type: !61) bpftrace-0.23.2/tests/codegen/llvm/tuple_bytearray.ll000066400000000000000000000201011477746507000226630ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %uint8_usym_t_int64__tuple_t = type { i8, [16 x i8], i64 } %usym_t = type { i64, i32, i32 } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_t = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !26 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !40 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !56 { entry: %"@t_key" = alloca i64, align 8 %tuple = alloca %uint8_usym_t_int64__tuple_t, align 8 %usym = alloca %usym_t, align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 16 %reg_ip = load volatile i64, ptr %2, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %usym) %get_pid_tgid = call i64 inttoptr (i64 14 to ptr)() %3 = lshr i64 %get_pid_tgid, 32 %pid = trunc i64 %3 to i32 %4 = getelementptr %usym_t, ptr %usym, i64 0, i32 0 %5 = getelementptr %usym_t, ptr %usym, i64 0, i32 1 %6 = getelementptr %usym_t, ptr %usym, i64 0, i32 2 store i64 %reg_ip, ptr %4, align 8 store i32 %pid, ptr %5, align 4 store i32 0, ptr %6, align 4 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 32, i1 false) %7 = getelementptr %uint8_usym_t_int64__tuple_t, ptr %tuple, i32 0, i32 0 store i8 1, ptr %7, align 1 %8 = getelementptr %uint8_usym_t_int64__tuple_t, ptr %tuple, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %8, ptr align 1 %usym, i64 16, i1 false) %9 = getelementptr %uint8_usym_t_int64__tuple_t, ptr %tuple, i32 0, i32 2 store i64 10, ptr %9, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@t_key") store i64 0, ptr %"@t_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_t, ptr %"@t_key", ptr %tuple, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@t_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #4 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!53} !llvm.module.flags = !{!55} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_t", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !18) !18 = !{!19, !21, !25} !19 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !20, size: 8) !20 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !21 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !22, size: 128, offset: 8) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !20, size: 128, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 16, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !14, size: 64, offset: 192) !26 = !DIGlobalVariableExpression(var: !27, expr: !DIExpression()) !27 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !28, isLocal: false, isDefinition: true) !28 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !29) !29 = !{!30, !35} !30 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !31, size: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 27, lowerBound: 0) !35 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !36, size: 64, offset: 64) !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !37, size: 64) !37 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !38) !38 = !{!39} !39 = !DISubrange(count: 262144, lowerBound: 0) !40 = !DIGlobalVariableExpression(var: !41, expr: !DIExpression()) !41 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !42, isLocal: false, isDefinition: true) !42 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !43) !43 = !{!44, !11, !49, !52} !44 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !45, size: 64) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !47) !47 = !{!48} !48 = !DISubrange(count: 2, lowerBound: 0) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !52 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !53 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !54) !54 = !{!0, !26, !40} !55 = !{i32 2, !"Debug Info Version", i32 3} !56 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !57, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !53, retainedNodes: !60) !57 = !DISubroutineType(types: !58) !58 = !{!14, !59} !59 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !20, size: 64) !60 = !{!61} !61 = !DILocalVariable(name: "ctx", arg: 1, scope: !56, file: !2, type: !59) bpftrace-0.23.2/tests/codegen/llvm/tuple_map_val_different_sizes.ll000066400000000000000000000211411477746507000255500ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } %"int64_string[13]__tuple_t" = type { i64, [13 x i8] } %"int64_string[3]__tuple_t" = type { i64, [3 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_a = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 @hi = global [3 x i8] c"hi\00" @hellolongstr = global [13 x i8] c"hellolongstr\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !55 { entry: %"@a_key2" = alloca i64, align 8 %tuple1 = alloca %"int64_string[13]__tuple_t", align 8 %"@a_val" = alloca %"int64_string[13]__tuple_t", align 8 %"@a_key" = alloca i64, align 8 %tuple = alloca %"int64_string[3]__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 16, i1 false) %1 = getelementptr %"int64_string[3]__tuple_t", ptr %tuple, i32 0, i32 0 store i64 1, ptr %1, align 8 %2 = getelementptr %"int64_string[3]__tuple_t", ptr %tuple, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %2, ptr align 1 @hi, i64 3, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@a_key") store i64 0, ptr %"@a_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@a_val") call void @llvm.memset.p0.i64(ptr align 1 %"@a_val", i8 0, i64 24, i1 false) %3 = getelementptr [16 x i8], ptr %tuple, i64 0, i64 0 %4 = getelementptr %"int64_string[13]__tuple_t", ptr %"@a_val", i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %4, ptr align 1 %3, i64 8, i1 false) %5 = getelementptr [16 x i8], ptr %tuple, i64 0, i64 8 %6 = getelementptr %"int64_string[13]__tuple_t", ptr %"@a_val", i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %5, i64 3, i1 false) %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_a, ptr %"@a_key", ptr %"@a_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@a_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@a_key") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple1) call void @llvm.memset.p0.i64(ptr align 1 %tuple1, i8 0, i64 24, i1 false) %7 = getelementptr %"int64_string[13]__tuple_t", ptr %tuple1, i32 0, i32 0 store i64 1, ptr %7, align 8 %8 = getelementptr %"int64_string[13]__tuple_t", ptr %tuple1, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %8, ptr align 1 @hellolongstr, i64 13, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@a_key2") store i64 0, ptr %"@a_key2", align 8 %update_elem3 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_a, ptr %"@a_key2", ptr %tuple1, i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@a_key2") call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple1) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!52} !llvm.module.flags = !{!54} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_a", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 192, elements: !18) !18 = !{!19, !20} !19 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !14, size: 64) !20 = !DIDerivedType(tag: DW_TAG_member, scope: !2, file: !2, baseType: !21, size: 104, offset: 64) !21 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 104, elements: !23) !22 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !23 = !{!24} !24 = !DISubrange(count: 13, lowerBound: 0) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !11, !48, !51} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !49, size: 64, offset: 128) !49 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !50, size: 64) !50 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !51 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !52 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !53) !53 = !{!0, !25, !39} !54 = !{i32 2, !"Debug Info Version", i32 3} !55 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !52, retainedNodes: !59) !56 = !DISubroutineType(types: !57) !57 = !{!14, !58} !58 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !55, file: !2, type: !58) bpftrace-0.23.2/tests/codegen/llvm/tuple_scratch_buf.ll000066400000000000000000000141101477746507000231470ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"int64_string[5]__tuple_t" = type { i64, [5 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @tuple_buf = dso_local externally_initialized global [1 x [1 x [16 x i8]]] zeroinitializer, section ".data.tuple_buf", !dbg !38 @xxxx = global [5 x i8] c"xxxx\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !49 { entry: %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %1 %2 = getelementptr [1 x [1 x [16 x i8]]], ptr @tuple_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 call void @llvm.memset.p0.i64(ptr align 1 %2, i8 0, i64 16, i1 false) %3 = getelementptr %"int64_string[5]__tuple_t", ptr %2, i32 0, i32 0 store i64 1, ptr %3, align 8 %4 = getelementptr %"int64_string[5]__tuple_t", ptr %2, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %4, ptr align 1 @xxxx, i64 5, i1 false) ret i64 0 } ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "tuple_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 128, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 128, elements: !28) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 128, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 16, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/tuple_stack.ll000066400000000000000000000127211477746507000217770ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"int64_string[5]__tuple_t" = type { i64, [5 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @xxxx = global [5 x i8] c"xxxx\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %tuple = alloca %"int64_string[5]__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 16, i1 false) %1 = getelementptr %"int64_string[5]__tuple_t", ptr %tuple, i32 0, i32 0 store i64 1, ptr %1, align 8 %2 = getelementptr %"int64_string[5]__tuple_t", ptr %tuple, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %2, ptr align 1 @xxxx, i64 5, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/tuple_variable_different_sizes.ll000066400000000000000000000155261477746507000257300ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"int64_string[13]__tuple_t" = type { i64, [13 x i8] } %"int64_string[3]__tuple_t" = type { i64, [3 x i8] } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @hi = global [3 x i8] c"hi\00" @hellolongstr = global [13 x i8] c"hellolongstr\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %tuple1 = alloca %"int64_string[13]__tuple_t", align 8 %"$t" = alloca %"int64_string[13]__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$t") call void @llvm.memset.p0.i64(ptr align 1 %"$t", i8 0, i64 24, i1 false) %tuple = alloca %"int64_string[3]__tuple_t", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple) call void @llvm.memset.p0.i64(ptr align 1 %tuple, i8 0, i64 16, i1 false) %1 = getelementptr %"int64_string[3]__tuple_t", ptr %tuple, i32 0, i32 0 store i64 1, ptr %1, align 8 %2 = getelementptr %"int64_string[3]__tuple_t", ptr %tuple, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %2, ptr align 1 @hi, i64 3, i1 false) call void @llvm.memset.p0.i64(ptr align 1 %"$t", i8 0, i64 24, i1 false) %3 = getelementptr [16 x i8], ptr %tuple, i64 0, i64 0 %4 = getelementptr %"int64_string[13]__tuple_t", ptr %"$t", i32 0, i32 0 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %4, ptr align 1 %3, i64 8, i1 false) %5 = getelementptr [16 x i8], ptr %tuple, i64 0, i64 8 %6 = getelementptr %"int64_string[13]__tuple_t", ptr %"$t", i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %5, i64 3, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple) call void @llvm.lifetime.start.p0(i64 -1, ptr %tuple1) call void @llvm.memset.p0.i64(ptr align 1 %tuple1, i8 0, i64 24, i1 false) %7 = getelementptr %"int64_string[13]__tuple_t", ptr %tuple1, i32 0, i32 0 store i64 1, ptr %7, align 8 %8 = getelementptr %"int64_string[13]__tuple_t", ptr %tuple1, i32 0, i32 1 call void @llvm.memcpy.p0.p0.i64(ptr align 1 %8, ptr align 1 @hellolongstr, i64 13, i1 false) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$t", ptr align 1 %tuple1, i64 24, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %tuple1) ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/unroll.ll000066400000000000000000000322751477746507000210020ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_i = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !45 { entry: %"@i_val43" = alloca i64, align 8 %"@i_key42" = alloca i64, align 8 %lookup_elem_val40 = alloca i64, align 8 %"@i_key35" = alloca i64, align 8 %"@i_val33" = alloca i64, align 8 %"@i_key32" = alloca i64, align 8 %lookup_elem_val30 = alloca i64, align 8 %"@i_key25" = alloca i64, align 8 %"@i_val23" = alloca i64, align 8 %"@i_key22" = alloca i64, align 8 %lookup_elem_val20 = alloca i64, align 8 %"@i_key15" = alloca i64, align 8 %"@i_val13" = alloca i64, align 8 %"@i_key12" = alloca i64, align 8 %lookup_elem_val10 = alloca i64, align 8 %"@i_key5" = alloca i64, align 8 %"@i_val3" = alloca i64, align 8 %"@i_key2" = alloca i64, align 8 %lookup_elem_val = alloca i64, align 8 %"@i_key1" = alloca i64, align 8 %"@i_val" = alloca i64, align 8 %"@i_key" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key") store i64 0, ptr %"@i_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_val") store i64 0, ptr %"@i_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_i, ptr %"@i_key", ptr %"@i_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key1") store i64 0, ptr %"@i_key1", align 8 %lookup_elem = call ptr inttoptr (i64 1 to ptr)(ptr @AT_i, ptr %"@i_key1") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val) %map_lookup_cond = icmp ne ptr %lookup_elem, null br i1 %map_lookup_cond, label %lookup_success, label %lookup_failure lookup_success: ; preds = %entry %1 = load i64, ptr %lookup_elem, align 8 store i64 %1, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_failure: ; preds = %entry store i64 0, ptr %lookup_elem_val, align 8 br label %lookup_merge lookup_merge: ; preds = %lookup_failure, %lookup_success %2 = load i64, ptr %lookup_elem_val, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key1") %3 = add i64 %2, 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key2") store i64 0, ptr %"@i_key2", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_val3") store i64 %3, ptr %"@i_val3", align 8 %update_elem4 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_i, ptr %"@i_key2", ptr %"@i_val3", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_val3") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key2") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key5") store i64 0, ptr %"@i_key5", align 8 %lookup_elem6 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_i, ptr %"@i_key5") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val10) %map_lookup_cond11 = icmp ne ptr %lookup_elem6, null br i1 %map_lookup_cond11, label %lookup_success7, label %lookup_failure8 lookup_success7: ; preds = %lookup_merge %4 = load i64, ptr %lookup_elem6, align 8 store i64 %4, ptr %lookup_elem_val10, align 8 br label %lookup_merge9 lookup_failure8: ; preds = %lookup_merge store i64 0, ptr %lookup_elem_val10, align 8 br label %lookup_merge9 lookup_merge9: ; preds = %lookup_failure8, %lookup_success7 %5 = load i64, ptr %lookup_elem_val10, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val10) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key5") %6 = add i64 %5, 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key12") store i64 0, ptr %"@i_key12", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_val13") store i64 %6, ptr %"@i_val13", align 8 %update_elem14 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_i, ptr %"@i_key12", ptr %"@i_val13", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_val13") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key12") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key15") store i64 0, ptr %"@i_key15", align 8 %lookup_elem16 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_i, ptr %"@i_key15") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val20) %map_lookup_cond21 = icmp ne ptr %lookup_elem16, null br i1 %map_lookup_cond21, label %lookup_success17, label %lookup_failure18 lookup_success17: ; preds = %lookup_merge9 %7 = load i64, ptr %lookup_elem16, align 8 store i64 %7, ptr %lookup_elem_val20, align 8 br label %lookup_merge19 lookup_failure18: ; preds = %lookup_merge9 store i64 0, ptr %lookup_elem_val20, align 8 br label %lookup_merge19 lookup_merge19: ; preds = %lookup_failure18, %lookup_success17 %8 = load i64, ptr %lookup_elem_val20, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val20) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key15") %9 = add i64 %8, 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key22") store i64 0, ptr %"@i_key22", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_val23") store i64 %9, ptr %"@i_val23", align 8 %update_elem24 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_i, ptr %"@i_key22", ptr %"@i_val23", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_val23") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key22") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key25") store i64 0, ptr %"@i_key25", align 8 %lookup_elem26 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_i, ptr %"@i_key25") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val30) %map_lookup_cond31 = icmp ne ptr %lookup_elem26, null br i1 %map_lookup_cond31, label %lookup_success27, label %lookup_failure28 lookup_success27: ; preds = %lookup_merge19 %10 = load i64, ptr %lookup_elem26, align 8 store i64 %10, ptr %lookup_elem_val30, align 8 br label %lookup_merge29 lookup_failure28: ; preds = %lookup_merge19 store i64 0, ptr %lookup_elem_val30, align 8 br label %lookup_merge29 lookup_merge29: ; preds = %lookup_failure28, %lookup_success27 %11 = load i64, ptr %lookup_elem_val30, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val30) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key25") %12 = add i64 %11, 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key32") store i64 0, ptr %"@i_key32", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_val33") store i64 %12, ptr %"@i_val33", align 8 %update_elem34 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_i, ptr %"@i_key32", ptr %"@i_val33", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_val33") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key32") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key35") store i64 0, ptr %"@i_key35", align 8 %lookup_elem36 = call ptr inttoptr (i64 1 to ptr)(ptr @AT_i, ptr %"@i_key35") call void @llvm.lifetime.start.p0(i64 -1, ptr %lookup_elem_val40) %map_lookup_cond41 = icmp ne ptr %lookup_elem36, null br i1 %map_lookup_cond41, label %lookup_success37, label %lookup_failure38 lookup_success37: ; preds = %lookup_merge29 %13 = load i64, ptr %lookup_elem36, align 8 store i64 %13, ptr %lookup_elem_val40, align 8 br label %lookup_merge39 lookup_failure38: ; preds = %lookup_merge29 store i64 0, ptr %lookup_elem_val40, align 8 br label %lookup_merge39 lookup_merge39: ; preds = %lookup_failure38, %lookup_success37 %14 = load i64, ptr %lookup_elem_val40, align 8 call void @llvm.lifetime.end.p0(i64 -1, ptr %lookup_elem_val40) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key35") %15 = add i64 %14, 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_key42") store i64 0, ptr %"@i_key42", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@i_val43") store i64 %15, ptr %"@i_val43", align 8 %update_elem44 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_i, ptr %"@i_key42", ptr %"@i_val43", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_val43") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@i_key42") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_i", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/unroll_async_id.ll000066400000000000000000000114171477746507000226460ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !39 { entry: %"$i" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$i") store i64 0, ptr %"$i", align 8 store i64 0, ptr %"$i", align 8 %1 = load i64, ptr %"$i", align 8 %2 = add i64 %1, 1 store i64 %2, ptr %"$i", align 8 %3 = load i64, ptr %"$i", align 8 %4 = add i64 %3, 1 store i64 %4, ptr %"$i", align 8 %5 = load i64, ptr %"$i", align 8 %6 = add i64 %5, 1 store i64 %6, ptr %"$i", align 8 %7 = load i64, ptr %"$i", align 8 %8 = add i64 %7, 1 store i64 %8, ptr %"$i", align 8 %9 = load i64, ptr %"$i", align 8 %10 = add i64 %9, 1 store i64 %10, ptr %"$i", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/variable.ll000066400000000000000000000164521477746507000212530ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } %"struct map_t.1" = type { ptr, ptr } %"struct map_t.2" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @AT_y = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @ringbuf = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !23 @event_loss_counter = dso_local global %"struct map_t.2" zeroinitializer, section ".maps", !dbg !37 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !53 { entry: %"@y_key" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %"$var" = alloca [16 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var") call void @llvm.memset.p0.i64(ptr align 1 %"$var", i8 0, i64 16, i1 false) %comm = alloca [16 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %comm) call void @llvm.memset.p0.i64(ptr align 1 %comm, i8 0, i64 16, i1 false) %get_comm = call i64 inttoptr (i64 16 to ptr)(ptr %comm, i64 16) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$var", ptr align 1 %comm, i64 16, i1 false) call void @llvm.lifetime.end.p0(i64 -1, ptr %comm) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"$var", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@y_key") store i64 0, ptr %"@y_key", align 8 %update_elem1 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_y, ptr %"@y_key", ptr %"$var", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@y_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!50} !llvm.module.flags = !{!52} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 128, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 16, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "AT_y", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !23 = !DIGlobalVariableExpression(var: !24, expr: !DIExpression()) !24 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !25, isLocal: false, isDefinition: true) !25 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !26) !26 = !{!27, !32} !27 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !28, size: 64) !28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64) !29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !30) !30 = !{!31} !31 = !DISubrange(count: 27, lowerBound: 0) !32 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !33, size: 64, offset: 64) !33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !34, size: 64) !34 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !35) !35 = !{!36} !36 = !DISubrange(count: 262144, lowerBound: 0) !37 = !DIGlobalVariableExpression(var: !38, expr: !DIExpression()) !38 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !39, isLocal: false, isDefinition: true) !39 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !40) !40 = !{!41, !11, !46, !49} !41 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !42, size: 64) !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !44) !44 = !{!45} !45 = !DISubrange(count: 2, lowerBound: 0) !46 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !47, size: 64, offset: 128) !47 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !48, size: 64) !48 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !49 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !50 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !51) !51 = !{!0, !21, !23, !37} !52 = !{i32 2, !"Debug Info Version", i32 3} !53 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !54, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !50, retainedNodes: !57) !54 = !DISubroutineType(types: !55) !55 = !{!14, !56} !56 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !57 = !{!58} !58 = !DILocalVariable(name: "ctx", arg: 1, scope: !53, file: !2, type: !56) bpftrace-0.23.2/tests/codegen/llvm/variable_assign_array.ll000066400000000000000000000151771477746507000240200ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !45 { entry: %"@x_val" = alloca i64, align 8 %"@x_key" = alloca i64, align 8 %array_access = alloca i32, align 4 %"$var" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var") store i64 0, ptr %"$var", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = inttoptr i64 %arg0 to ptr %4 = call ptr @llvm.preserve.static.offset(ptr %3) %5 = getelementptr i8, ptr %4, i64 0 %6 = ptrtoint ptr %5 to i64 store i64 %6, ptr %"$var", align 8 %7 = load i64, ptr %"$var", align 8 %8 = inttoptr i64 %7 to ptr %9 = call ptr @llvm.preserve.static.offset(ptr %8) %10 = getelementptr i8, ptr %9, i64 0 call void @llvm.lifetime.start.p0(i64 -1, ptr %array_access) %probe_read_kernel = call i64 inttoptr (i64 113 to ptr)(ptr %array_access, i32 4, ptr %10) %11 = load i32, ptr %array_access, align 4 call void @llvm.lifetime.end.p0(i64 -1, ptr %array_access) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_key") store i64 0, ptr %"@x_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") %12 = sext i32 %11 to i64 store i64 %12, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"@x_key", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) bpftrace-0.23.2/tests/codegen/llvm/variable_assign_string.ll000066400000000000000000000147061477746507000242050ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_map = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @blah = global [5 x i8] c"blah\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@map_key" = alloca i64, align 8 %"$var" = alloca [5 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var") call void @llvm.memset.p0.i64(ptr align 1 %"$var", i8 0, i64 5, i1 false) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$var", ptr align 1 @blah, i64 5, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_key") store i64 0, ptr %"@map_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_map, ptr %"@map_key", ptr %"$var", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_map", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 40, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 5, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/variable_assign_string_shorter.ll000066400000000000000000000152131477746507000257450ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_map = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !21 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !35 @xxxxx = global [6 x i8] c"xxxxx\00" @a = global [2 x i8] c"a\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !51 { entry: %"@map_key" = alloca i64, align 8 %"$var" = alloca [6 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$var") call void @llvm.memset.p0.i64(ptr align 1 %"$var", i8 0, i64 6, i1 false) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$var", ptr align 1 @xxxxx, i64 6, i1 false) call void @llvm.memset.p0.i64(ptr align 1 %"$var", i8 0, i64 6, i1 false) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$var", ptr align 1 @a, i64 2, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@map_key") store i64 0, ptr %"@map_key", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_map, ptr %"@map_key", ptr %"$var", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@map_key") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!48} !llvm.module.flags = !{!50} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_map", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !16, size: 64, offset: 192) !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) !17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 48, elements: !19) !18 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !19 = !{!20} !20 = !DISubrange(count: 6, lowerBound: 0) !21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression()) !22 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !23, isLocal: false, isDefinition: true) !23 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !24) !24 = !{!25, !30} !25 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !26, size: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 27, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !31, size: 64, offset: 64) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !33) !33 = !{!34} !34 = !DISubrange(count: 262144, lowerBound: 0) !35 = !DIGlobalVariableExpression(var: !36, expr: !DIExpression()) !36 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !37, isLocal: false, isDefinition: true) !37 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !38) !38 = !{!39, !11, !44, !47} !39 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !40, size: 64) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !42) !42 = !{!43} !43 = !DISubrange(count: 2, lowerBound: 0) !44 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !45, size: 64, offset: 128) !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64) !46 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !47 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !48 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !49) !49 = !{!0, !21, !35} !50 = !{i32 2, !"Debug Info Version", i32 3} !51 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !52, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !48, retainedNodes: !55) !52 = !DISubroutineType(types: !53) !53 = !{!14, !54} !54 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !55 = !{!56} !56 = !DILocalVariable(name: "ctx", arg: 1, scope: !51, file: !2, type: !54) bpftrace-0.23.2/tests/codegen/llvm/variable_map_key_lifetime.ll000066400000000000000000000156671477746507000246450ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_x = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !25 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !39 @abc = global [4 x i8] c"abc\00" ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !55 { entry: %"@x_val1" = alloca i64, align 8 %"@x_val" = alloca i64, align 8 %"$myvar" = alloca [4 x i8], align 1 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$myvar") call void @llvm.memset.p0.i64(ptr align 1 %"$myvar", i8 0, i64 4, i1 false) call void @llvm.memcpy.p0.p0.i64(ptr align 1 %"$myvar", ptr align 1 @abc, i64 4, i1 false) call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val") store i64 1, ptr %"@x_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"$myvar", ptr %"@x_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val") call void @llvm.lifetime.start.p0(i64 -1, ptr %"@x_val1") store i64 1, ptr %"@x_val1", align 8 %update_elem2 = call i64 inttoptr (i64 2 to ptr)(ptr @AT_x, ptr %"$myvar", ptr %"@x_val1", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@x_val1") ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) declare void @llvm.memset.p0.i64(ptr nocapture writeonly %0, i8 %1, i64 %2, i1 immarg %3) #2 ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!52} !llvm.module.flags = !{!54} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_x", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !16, !22} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 131072, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 4096, lowerBound: 0) !16 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !17, size: 64, offset: 128) !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 32, elements: !20) !19 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !20 = !{!21} !21 = !DISubrange(count: 4, lowerBound: 0) !22 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !23, size: 64, offset: 192) !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64) !24 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !25 = !DIGlobalVariableExpression(var: !26, expr: !DIExpression()) !26 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !27, isLocal: false, isDefinition: true) !27 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !28) !28 = !{!29, !34} !29 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !30, size: 64) !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64) !31 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !32) !32 = !{!33} !33 = !DISubrange(count: 27, lowerBound: 0) !34 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !35, size: 64, offset: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 262144, lowerBound: 0) !39 = !DIGlobalVariableExpression(var: !40, expr: !DIExpression()) !40 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !41, isLocal: false, isDefinition: true) !41 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !42) !42 = !{!43, !48, !49, !22} !43 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !44, size: 64) !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) !45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !46) !46 = !{!47} !47 = !DISubrange(count: 2, lowerBound: 0) !48 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !50, size: 64, offset: 128) !50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) !51 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !52 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !53) !53 = !{!0, !25, !39} !54 = !{i32 2, !"Debug Info Version", i32 3} !55 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !52, retainedNodes: !59) !56 = !DISubroutineType(types: !57) !57 = !{!24, !58} !58 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) !59 = !{!60} !60 = !DILocalVariable(name: "ctx", arg: 1, scope: !55, file: !2, type: !58) bpftrace-0.23.2/tests/codegen/llvm/variable_post_dec.ll000066400000000000000000000110701477746507000231220ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !39 { entry: %"$y" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$y") store i64 0, ptr %"$y", align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 store i64 10, ptr %"$x", align 8 %1 = load i64, ptr %"$x", align 8 %2 = sub i64 %1, 1 store i64 %2, ptr %"$x", align 8 store i64 %1, ptr %"$y", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/variable_post_inc.ll000066400000000000000000000110701477746507000231400ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !39 { entry: %"$y" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$y") store i64 0, ptr %"$y", align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 store i64 10, ptr %"$x", align 8 %1 = load i64, ptr %"$x", align 8 %2 = add i64 %1, 1 store i64 %2, ptr %"$x", align 8 store i64 %1, ptr %"$y", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/variable_pre_dec.ll000066400000000000000000000110701477746507000227230ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !39 { entry: %"$y" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$y") store i64 0, ptr %"$y", align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 store i64 10, ptr %"$x", align 8 %1 = load i64, ptr %"$x", align 8 %2 = sub i64 %1, 1 store i64 %2, ptr %"$x", align 8 store i64 %2, ptr %"$y", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/variable_pre_inc.ll000066400000000000000000000110701477746507000227410ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @BEGIN_1(ptr %0) section "s_BEGIN_1" !dbg !39 { entry: %"$y" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$y") store i64 0, ptr %"$y", align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 store i64 10, ptr %"$x", align 8 %1 = load i64, ptr %"$x", align 8 %2 = add i64 %1, 1 store i64 %2, ptr %"$x", align 8 store i64 %2, ptr %"$y", align 8 ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "BEGIN_1", linkageName: "BEGIN_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/variable_scratch_buf.ll000066400000000000000000000143171477746507000236140ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @max_cpu_id = dso_local externally_initialized constant i64 zeroinitializer, section ".rodata", !dbg !36 @var_buf = dso_local externally_initialized global [1 x [2 x [8 x i8]]] zeroinitializer, section ".data.var_buf", !dbg !38 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !49 { entry: %get_cpu_id1 = call i64 inttoptr (i64 8 to ptr)() %1 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded2 = and i64 %get_cpu_id1, %1 %2 = getelementptr [1 x [2 x [8 x i8]]], ptr @var_buf, i64 0, i64 %cpu.id.bounded2, i64 1, i64 0 store i64 0, ptr %2, align 8 %get_cpu_id = call i64 inttoptr (i64 8 to ptr)() %3 = load i64, ptr @max_cpu_id, align 8 %cpu.id.bounded = and i64 %get_cpu_id, %3 %4 = getelementptr [1 x [2 x [8 x i8]]], ptr @var_buf, i64 0, i64 %cpu.id.bounded, i64 0, i64 0 store i64 0, ptr %4, align 8 %5 = call ptr @llvm.preserve.static.offset(ptr %0) %6 = getelementptr i64, ptr %5, i64 14 %arg0 = load volatile i64, ptr %6, align 8 %7 = icmp ugt i64 %arg0, 0 %true_cond = icmp ne i1 %7, false br i1 %true_cond, label %if_body, label %else_body if_body: ; preds = %entry store i64 1, ptr %4, align 8 br label %if_end if_end: ; preds = %else_body, %if_body ret i64 0 else_body: ; preds = %entry store i64 2, ptr %2, align 8 br label %if_end } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } !llvm.dbg.cu = !{!46} !llvm.module.flags = !{!48} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = !DIGlobalVariableExpression(var: !37, expr: !DIExpression()) !37 = distinct !DIGlobalVariable(name: "max_cpu_id", linkageName: "global", scope: !2, file: !2, type: !35, isLocal: false, isDefinition: true) !38 = !DIGlobalVariableExpression(var: !39, expr: !DIExpression()) !39 = distinct !DIGlobalVariable(name: "var_buf", linkageName: "global", scope: !2, file: !2, type: !40, isLocal: false, isDefinition: true) !40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 128, elements: !28) !41 = !DICompositeType(tag: DW_TAG_array_type, baseType: !42, size: 128, elements: !23) !42 = !DICompositeType(tag: DW_TAG_array_type, baseType: !43, size: 64, elements: !44) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DISubrange(count: 8, lowerBound: 0) !46 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !47) !47 = !{!0, !16, !36, !38} !48 = !{i32 2, !"Debug Info Version", i32 3} !49 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !46, retainedNodes: !53) !50 = !DISubroutineType(types: !51) !51 = !{!35, !52} !52 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !53 = !{!54} !54 = !DILocalVariable(name: "ctx", arg: 1, scope: !49, file: !2, type: !52) bpftrace-0.23.2/tests/codegen/llvm/variable_stack.ll000066400000000000000000000123351477746507000224340ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr } %"struct map_t.0" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @ringbuf = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @event_loss_counter = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @kprobe_f_1(ptr %0) section "s_kprobe_f_1" !dbg !39 { entry: %"$x1" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x1") store i64 0, ptr %"$x1", align 8 %"$x" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$x") store i64 0, ptr %"$x", align 8 %1 = call ptr @llvm.preserve.static.offset(ptr %0) %2 = getelementptr i64, ptr %1, i64 14 %arg0 = load volatile i64, ptr %2, align 8 %3 = icmp ugt i64 %arg0, 0 %true_cond = icmp ne i1 %3, false br i1 %true_cond, label %if_body, label %else_body if_body: ; preds = %entry store i64 1, ptr %"$x", align 8 br label %if_end if_end: ; preds = %else_body, %if_body ret i64 0 else_body: ; preds = %entry store i64 2, ptr %"$x1", align 8 br label %if_end } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare ptr @llvm.preserve.static.offset(ptr readnone %0) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #2 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!36} !llvm.module.flags = !{!38} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !4) !4 = !{!5, !11} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 27, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !12, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) !13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !14) !14 = !{!15} !15 = !DISubrange(count: 262144, lowerBound: 0) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !19) !19 = !{!20, !25, !30, !33} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 2, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 1, lowerBound: 0) !30 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !31, size: 64, offset: 128) !31 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !32, size: 64) !32 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !33 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !34, size: 64, offset: 192) !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 64) !35 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !36 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !37) !37 = !{!0, !16} !38 = !{i32 2, !"Debug Info Version", i32 3} !39 = distinct !DISubprogram(name: "kprobe_f_1", linkageName: "kprobe_f_1", scope: !2, file: !2, type: !40, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !36, retainedNodes: !44) !40 = !DISubroutineType(types: !41) !41 = !{!35, !42} !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) !43 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !44 = !{!45} !45 = !DILocalVariable(name: "ctx", arg: 1, scope: !39, file: !2, type: !42) bpftrace-0.23.2/tests/codegen/llvm/while_loop_no_unroll.ll000066400000000000000000000143171477746507000237140ustar00rootroot00000000000000; 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" %"struct map_t" = type { ptr, ptr, ptr, ptr } %"struct map_t.0" = type { ptr, ptr } %"struct map_t.1" = type { ptr, ptr, ptr, ptr } @LICENSE = global [4 x i8] c"GPL\00", section "license" @AT_ = dso_local global %"struct map_t" zeroinitializer, section ".maps", !dbg !0 @ringbuf = dso_local global %"struct map_t.0" zeroinitializer, section ".maps", !dbg !16 @event_loss_counter = dso_local global %"struct map_t.1" zeroinitializer, section ".maps", !dbg !30 ; Function Attrs: nounwind declare i64 @llvm.bpf.pseudo(i64 %0, i64 %1) #0 define i64 @interval_s_1_1(ptr %0) section "s_interval_s_1_1" !dbg !45 { entry: %"@_val" = alloca i64, align 8 %"@_key" = alloca i64, align 8 %"$a" = alloca i64, align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"$a") store i64 0, ptr %"$a", align 8 store i64 0, ptr %"$a", align 8 br label %while_cond while_cond: ; preds = %while_body, %entry %1 = load i64, ptr %"$a", align 8 %2 = icmp sle i64 %1, 10 %true_cond = icmp ne i1 %2, false br i1 %true_cond, label %while_body, label %while_end, !llvm.loop !52 while_body: ; preds = %while_cond %3 = load i64, ptr %"$a", align 8 %4 = add i64 %3, 1 store i64 %4, ptr %"$a", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_key") store i64 0, ptr %"@_key", align 8 call void @llvm.lifetime.start.p0(i64 -1, ptr %"@_val") store i64 %3, ptr %"@_val", align 8 %update_elem = call i64 inttoptr (i64 2 to ptr)(ptr @AT_, ptr %"@_key", ptr %"@_val", i64 0) call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_val") call void @llvm.lifetime.end.p0(i64 -1, ptr %"@_key") br label %while_cond while_end: ; preds = %while_cond ret i64 0 } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.start.p0(i64 immarg %0, ptr nocapture %1) #1 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) declare void @llvm.lifetime.end.p0(i64 immarg %0, ptr nocapture %1) #1 attributes #0 = { nounwind } attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } !llvm.dbg.cu = !{!42} !llvm.module.flags = !{!44} !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "AT_", linkageName: "global", scope: !2, file: !2, type: !3, isLocal: false, isDefinition: true) !2 = !DIFile(filename: "bpftrace.bpf.o", directory: ".") !3 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !4) !4 = !{!5, !11, !12, !15} !5 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !6, size: 64) !6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) !7 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 32, elements: !9) !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !9 = !{!10} !10 = !DISubrange(count: 1, lowerBound: 0) !11 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !6, size: 64, offset: 64) !12 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !13, size: 64, offset: 128) !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) !14 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed) !15 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !2, file: !2, baseType: !13, size: 64, offset: 192) !16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) !17 = distinct !DIGlobalVariable(name: "ringbuf", linkageName: "global", scope: !2, file: !2, type: !18, isLocal: false, isDefinition: true) !18 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 128, elements: !19) !19 = !{!20, !25} !20 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !21, size: 64) !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64) !22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 864, elements: !23) !23 = !{!24} !24 = !DISubrange(count: 27, lowerBound: 0) !25 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !2, file: !2, baseType: !26, size: 64, offset: 64) !26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !27, size: 64) !27 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 8388608, elements: !28) !28 = !{!29} !29 = !DISubrange(count: 262144, lowerBound: 0) !30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression()) !31 = distinct !DIGlobalVariable(name: "event_loss_counter", linkageName: "global", scope: !2, file: !2, type: !32, isLocal: false, isDefinition: true) !32 = !DICompositeType(tag: DW_TAG_structure_type, scope: !2, file: !2, size: 256, elements: !33) !33 = !{!34, !11, !39, !15} !34 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !2, file: !2, baseType: !35, size: 64) !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) !36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 64, elements: !37) !37 = !{!38} !38 = !DISubrange(count: 2, lowerBound: 0) !39 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !2, file: !2, baseType: !40, size: 64, offset: 128) !40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) !41 = !DIBasicType(name: "int32", size: 32, encoding: DW_ATE_signed) !42 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "bpftrace", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, globals: !43) !43 = !{!0, !16, !30} !44 = !{i32 2, !"Debug Info Version", i32 3} !45 = distinct !DISubprogram(name: "interval_s_1_1", linkageName: "interval_s_1_1", scope: !2, file: !2, type: !46, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !42, retainedNodes: !50) !46 = !DISubroutineType(types: !47) !47 = !{!14, !48} !48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) !49 = !DIBasicType(name: "int8", size: 8, encoding: DW_ATE_signed) !50 = !{!51} !51 = !DILocalVariable(name: "ctx", arg: 1, scope: !45, file: !2, type: !48) !52 = distinct !{!52, !53} !53 = !{!"llvm.loop.unroll.disable"} bpftrace-0.23.2/tests/codegen/logical_and.cpp000066400000000000000000000003711477746507000211140ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, logical_and) { test("kprobe:f { @x = pid != 1234 && pid != 1235 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/logical_and_or_different_type.cpp000066400000000000000000000006341477746507000247050ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, logical_and_or_different_type) { test("struct Foo { int m; }" "BEGIN" "{" " $foo = *(struct Foo*)0;" " printf(\"%d %d %d %d\", $foo.m && 0, 1 && $foo.m, $foo.m || 0, 0 || " "$foo.m)" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/logical_not.cpp000066400000000000000000000003411477746507000211470ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, logical_not) { test("BEGIN { @x = !10; @y = !0; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/logical_or.cpp000066400000000000000000000003701477746507000207710ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, logical_or) { test("kprobe:f { @x = pid == 1234 || pid == 1235 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/macro_definition.cpp000066400000000000000000000003621477746507000221710ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, macro_definition) { test("#define FOO 100\nk:f { @ = FOO }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/map_args.cpp000066400000000000000000000006301477746507000204470ustar00rootroot00000000000000#ifdef HAVE_LIBLLDB #include "../dwarf_common.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { class codegen_dwarf : public test_dwarf {}; TEST_F(codegen_dwarf, map_args) { std::string uprobe = "uprobe:" + std::string(bin_) + ":func_1"; test(uprobe + "{ @ = args }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace #endif // HAVE_LIBLLDB bpftrace-0.23.2/tests/codegen/map_assign_array.cpp000066400000000000000000000005371477746507000222030ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_assign_array) { test("struct Foo { int arr[4]; }" "kprobe:f" "{" " @x[0] = ((struct Foo *)arg0)->arr;" " $var = @x[0][0]; " "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/map_assign_int.cpp000066400000000000000000000003441477746507000216530ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_assign_int) { test("kprobe:f { @x = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/map_assign_string.cpp000066400000000000000000000005361477746507000223720ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_assign_string) { test("kprobe:f { @x = \"blah\"; }", NAME); } TEST(codegen, map_assign_string_shorter) { test("kprobe:f { @x = \"xxxxx\"; @x = \"a\"; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/map_increment_decrement.cpp000066400000000000000000000004031477746507000235230ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_increment_decrement) { test("BEGIN { @x = 10; @x++; ++@x; @x--; --@x; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/map_key_array.cpp000066400000000000000000000005001477746507000214750ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_array) { test("struct Foo { int arr[4]; }" "kprobe:f" "{" " @x[((struct Foo *)arg0)->arr] = 44;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/map_key_int.cpp000066400000000000000000000003531477746507000211570ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_int) { test("kprobe:f { @x[11,22,33] = 44 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/map_key_probe.cpp000066400000000000000000000005021477746507000214700ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_probe) { test("tracepoint:sched:sched_one,tracepoint:sched:sched_two { @x[probe] = " "@x[probe] + 1 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/map_key_string.cpp000066400000000000000000000003621477746507000216730ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_string) { test("kprobe:f { @x[\"a\", \"b\"] = 44 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/map_key_struct.cpp000066400000000000000000000004701477746507000217110ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, map_key_struct) { test("struct Foo { int x; }" "kprobe:f" "{" " @x[*((struct Foo *)arg0)] = 44;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/multiple_identical_probes.cpp000066400000000000000000000003721477746507000241020ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, multiple_identical_probes) { test("kprobe:f { 1; } kprobe:f { 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/nested_array_struct.cpp000066400000000000000000000006111477746507000227410ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, nested_array_struct) { test("struct Bar { int x; } struct Foo { struct Bar bar[2][2]; }" "kprobe:f" "{" " @bar[42] = ((struct Foo *)arg0)->bar;" " @ = @bar[42][0][1].x;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/nested_while_loop.cpp000066400000000000000000000005241477746507000223630ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, nested_while_loop) { test(R"PROG( i:s:1 { $i=1; while ($i <= 100) { $j=0; $i++; while ($j <= 100 ) { @++; $j++; } } } )PROG", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/optional_positional_parameter.cpp000066400000000000000000000003661477746507000250120ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, optional_positional_parameter) { test("BEGIN { @x = $1; @y = str($2) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/per_cpu_map_cast.cpp000066400000000000000000000027531477746507000221720ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, count_cast) { test("kprobe:f { @x = count(); $res = @x; }", NAME); } TEST(codegen, sum_cast) { test("kprobe:f { @x = sum(2); $res = @x; }", NAME); } TEST(codegen, min_cast) { test("kprobe:f { @x = min(2); $res = @x; }", NAME); } TEST(codegen, max_cast) { test("kprobe:f { @x = max(2); $res = @x; }", NAME); } TEST(codegen, avg_cast) { test("kprobe:f { @x = avg(2); $res = @x; }", NAME); } TEST(codegen, count_cast_loop) { test("kprobe:f { @x[1] = count(); for ($kv : @x) { $res = $kv.1; } }", NAME); } TEST(codegen, sum_cast_loop) { test("kprobe:f { @x[1] = sum(2); for ($kv : @x) { $res = $kv.1; } }", NAME); } TEST(codegen, min_cast_loop) { test("kprobe:f { @x[1] = min(2); for ($kv : @x) { $res = $kv.1; } }", NAME); } TEST(codegen, max_cast_loop) { test("kprobe:f { @x[1] = max(2); for ($kv : @x) { $res = $kv.1; } }", NAME); } TEST(codegen, avg_cast_loop) { test("kprobe:f { @x[1] = avg(2); for ($kv : @x) { $res = $kv.1; } }", NAME); } TEST(codegen, count_no_cast_for_print) { test("BEGIN { @ = count(); print(@) }", NAME); } TEST(codegen, count_cast_loop_multi_key) { test("kprobe:f { @x[1, 2] = count(); for ($kv : @x) { $res = $kv.1; } }", NAME); } TEST(codegen, count_cast_loop_stack_key) { test("kprobe:f { @x[kstack(raw)] = count(); for ($kv : @x) { $res = $kv.1; } " "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/pointer_add_int.cpp000066400000000000000000000003621477746507000220220ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, pointer_add_int) { test("kprobe:f { $v = (int16*)1000; $v += 10; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/pointer_inc_map.cpp000066400000000000000000000003531477746507000220260ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, pointer_inc_map) { test("kprobe:f { @ = (int16*)1000; @++ }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/pointer_inc_var.cpp000066400000000000000000000003551477746507000220430ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, pointer_inc_var) { test("kprobe:f { $v = (int16*)1000; $v++ }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/pointer_truthy.cpp000066400000000000000000000010541477746507000217560ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, pointer_if_condition) { test("kprobe:f { $v = (int16*)1; if ($v) {} }", NAME); } TEST(codegen, pointer_tenary_expression) { test("kprobe:f { $v = (int16*)1; $x = $v ? 1 : 0 }", NAME); } TEST(codegen, pointer_logical_and) { test("kprobe:f { $v = (int16*)1; if ($v && 0) {} }", NAME); } TEST(codegen, pointer_logical_or) { test("kprobe:f { $v = (int16*)1; if ($v || 0) {} }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/pred_binop.cpp000066400000000000000000000003571477746507000210050ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, pred_binop) { test("kprobe:f / pid == 1234 / { @x = 1 }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/ptr_to_ptr.cpp000066400000000000000000000004121477746507000210500ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ptr_to_ptr) { test(R"PROG(kprobe:f { $pp = (int32 **)0; $res = **kptr($pp); })PROG", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/regression.cpp000066400000000000000000000014311477746507000210360ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { using ::testing::_; TEST(codegen, regression_957) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str("t:sched:sched_one* { cat(\"%s\", probe); }"), 0); ast::SemanticAnalyser semantics(driver.ctx, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.ctx, *bpftrace); auto resources_optional = resource_analyser.analyse(); ASSERT_TRUE(resources_optional.has_value()); bpftrace->resources = resources_optional.value(); ast::CodegenLLVM codegen(driver.ctx, *bpftrace); codegen.compile(); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/runtime_error_check.cpp000066400000000000000000000043131477746507000227110ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, runtime_error_check_lookup_percpu) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "kprobe:f { @ = count(); $a = @; }", NAME); } TEST(codegen, runtime_error_check_delete) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "kprobe:f { @x[1] = 1; delete(@x, 1) }", NAME); } TEST(codegen, runtime_error_check_pid_tid) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "kprobe:f { @x = pid; @y = tid }", NAME); } TEST(codegen, runtime_error_check_comm) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "kprobe:f { @x = comm; }", NAME); } TEST(codegen, runtime_error_check_signal) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; bpftrace->safe_mode_ = false; test(*bpftrace, "kprobe:f { signal(8); }", NAME); } TEST(codegen, runtime_error_check_path) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "fentry:filp_close { path((uint8 *)0); }", NAME); } TEST(codegen, runtime_error_check_printf) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "iter:task_file { printf(\"%d\", 1); }", NAME); } TEST(codegen, runtime_error_check_for_map) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "BEGIN { @map[16] = 32; for ($kv : @map) { @x = $kv; } }", NAME); } TEST(codegen, runtime_error_check_stack) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "kprobe:f { @x = ustack; @y = kstack }", NAME); } TEST(codegen, runtime_error_check_lookup_no_warning) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 1; test(*bpftrace, "kprobe:f { @++; }", NAME); } TEST(codegen, runtime_error_check_lookup) { auto bpftrace = get_mock_bpftrace(); bpftrace->helper_check_level_ = 2; test(*bpftrace, "kprobe:f { @++; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/scratch_buffer.cpp000066400000000000000000000112421477746507000216370ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { static constexpr uint64_t SMALL_ON_STACK_LIMIT = 0; static constexpr uint64_t LARGE_ON_STACK_LIMIT = 128; static constexpr uint64_t MAX_STRLEN = 64; static void test_stack_or_scratch_buffer(const std::string &input, const std::string &name, uint64_t on_stack_limit) { auto bpftrace = get_mock_bpftrace(); auto configs = ConfigSetter(bpftrace->config_, ConfigSource::script); configs.set(ConfigKeyInt::on_stack_limit, on_stack_limit); configs.set(ConfigKeyInt::max_strlen, MAX_STRLEN); bpftrace->safe_mode_ = true; test(*bpftrace, input, name); } TEST(codegen, tuple_scratch_buf) { test_stack_or_scratch_buffer("kprobe:f { (1, \"xxxx\") }", NAME, SMALL_ON_STACK_LIMIT); } TEST(codegen, tuple_stack) { test_stack_or_scratch_buffer("kprobe:f { (1, \"xxxx\") }", NAME, LARGE_ON_STACK_LIMIT); } TEST(codegen, fmt_str_args_scratch_buf) { test_stack_or_scratch_buffer("kprobe:f { printf(\"%s %d\\n\", \"xxxx\", 1) }", NAME, SMALL_ON_STACK_LIMIT); } TEST(codegen, fmt_str_args_stack) { test_stack_or_scratch_buffer("kprobe:f { printf(\"%s %d\\n\", \"xxxx\", 1) }", NAME, LARGE_ON_STACK_LIMIT); } TEST(codegen, str_scratch_buf) { test_stack_or_scratch_buffer("kprobe:f { str(arg0) }", NAME, SMALL_ON_STACK_LIMIT); } TEST(codegen, str_stack) { test_stack_or_scratch_buffer("kprobe:f { str(arg0) }", NAME, LARGE_ON_STACK_LIMIT); } TEST(codegen, map_value_int_scratch_buf) { test_stack_or_scratch_buffer("kprobe:f { @x = 1; @y = @x }", NAME, SMALL_ON_STACK_LIMIT); } TEST(codegen, map_value_int_stack) { test_stack_or_scratch_buffer("kprobe:f { @x = 1; @y = @x }", NAME, LARGE_ON_STACK_LIMIT); } // Using two tuples with different sizes will trigger copying tuples to the // scratch buffer or stack prior to updating the map TEST(codegen, map_value_tuple_scratch_buf) { test_stack_or_scratch_buffer( "kprobe:f { @x = (\"xxx\", 1); @x = (\"xxxxxxx\", 1); @y = @x }", NAME, SMALL_ON_STACK_LIMIT); } TEST(codegen, map_value_tuple_stack) { test_stack_or_scratch_buffer( "kprobe:f { @x = (\"xxx\", 1); @x = (\"xxxxxxx\", 1); @y = @x }", NAME, LARGE_ON_STACK_LIMIT); } // Use an if statement with same variable name with two scopes to ensure // initialization happens prior to both scopes TEST(codegen, variable_scratch_buf) { test_stack_or_scratch_buffer( "kprobe:f { if (arg0 > 0) { $x = 1; } else { $x = 2 } }", NAME, SMALL_ON_STACK_LIMIT); } TEST(codegen, variable_stack) { test_stack_or_scratch_buffer( "kprobe:f { if (arg0 > 0) { $x = 1; } else { $x = 2 } }", NAME, LARGE_ON_STACK_LIMIT); } TEST(codegen, map_key_scratch_buf) { test_stack_or_scratch_buffer("kprobe:f { @x[1] = 1; @y[\"yyyy\"] = @x[1]; }", NAME, SMALL_ON_STACK_LIMIT); } TEST(codegen, map_key_stack) { test_stack_or_scratch_buffer("kprobe:f { @x[1] = 1; @y[\"yyyy\"] = @x[1]; }", NAME, LARGE_ON_STACK_LIMIT); } // Test map keys with aggregation and map-related functions TEST(codegen, call_map_key_scratch_buf) { test_stack_or_scratch_buffer("kprobe:f { @x[1] = count(); @y = hist(10); " "has_key(@x, 1); delete(@x, 1); }", NAME, SMALL_ON_STACK_LIMIT); } TEST(codegen, call_map_key_stack) { test_stack_or_scratch_buffer("kprobe:f { @x[1] = count(); @y = hist(10); " "has_key(@x, 1); delete(@x, 1); }", NAME, LARGE_ON_STACK_LIMIT); } TEST(codegen, probe_str_scratch_buf) { test_stack_or_scratch_buffer("tracepoint:sched:sched_one { @x = probe }", NAME, SMALL_ON_STACK_LIMIT); } TEST(codegen, probe_str_stack) { test_stack_or_scratch_buffer("tracepoint:sched:sched_one { @x = probe }", NAME, LARGE_ON_STACK_LIMIT); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/self_probe.cpp000066400000000000000000000003741477746507000210030ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, self_probe) { test("self:signal:SIGUSR1 { @x = probe }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/strcontains.cpp000066400000000000000000000007441477746507000212330ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, strcontains) { test(R"(k:foo { strcontains("hello-test-world", "test") })", NAME); } TEST(codegen, strcontains_one_literal) { test(R"(k:foo { strcontains(str(arg0), "test") })", NAME); } TEST(codegen, strcontains_no_literals) { test(R"(k:foo { strcontains(str(arg0), str(arg1)) })", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/string_propagation.cpp000066400000000000000000000003571477746507000225750ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, string_propagation) { test("kprobe:f { @x = \"asdf\"; @y = @x }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/strncmp.cpp000066400000000000000000000012201477746507000203400ustar00rootroot00000000000000#include "../mocks.h" #include "common.h" using ::testing::_; using ::testing::Return; namespace bpftrace { namespace test { namespace codegen { TEST(codegen, strncmp_one_literal) { test("kprobe:f { @[strncmp(comm, \"sshd\", 2)] = 1; }", NAME); } TEST(codegen, strncmp_no_literals) { test("t:file:filename /str(args.filename) == comm/ { @=1 }", NAME); } TEST(codegen, string_equal_comparison) { test("kprobe:f { @[comm == \"sshd\"] = 1; }", NAME); } TEST(codegen, string_not_equal_comparison) { test("kprobe:f { @[comm != \"sshd\"] = 1; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_char.cpp000066400000000000000000000010061477746507000211750ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_char) { test("struct Foo { char x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { char x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_integer_ptr.cpp000066400000000000000000000010171477746507000226040ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_integer_ptr) { test("struct Foo { int *x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = *$foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { int *x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = *$foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_integers.cpp000066400000000000000000000010101477746507000220730ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_integers) { test("struct Foo { int x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { int x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_long.cpp000066400000000000000000000010061477746507000212170ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_long) { test("struct Foo { long x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { long x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_nested_struct_anon.cpp000066400000000000000000000010721477746507000241640ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_nested_struct_anon) { test("struct Foo { struct { int x; } bar; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.bar.x;" "}", std::string(NAME) + "_1"); test("struct Foo { struct { int x; } bar; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->bar.x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_nested_struct_named.cpp000066400000000000000000000011311477746507000243110ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_nested_struct_named) { test("struct Bar { int x; } struct Foo { struct Bar bar; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.bar.x;" "}", std::string(NAME) + "_1"); test("struct Bar { int x; } struct Foo { struct Bar bar; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->bar.x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_nested_struct_ptr_named.cpp000066400000000000000000000011411477746507000251770ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_nested_struct_ptr_named) { test("struct Bar { int x; } struct Foo { struct Bar *bar; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.bar->x;" "}", std::string(NAME) + "_1"); test("struct Bar { int x; } struct Foo { struct Bar *bar; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->bar->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_save.cpp000066400000000000000000000007401477746507000212220ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_save) { test("struct Foo { int x, y, z; }" "kprobe:f" "{" " @foo = *(struct Foo*)arg0;" "}", std::string(NAME) + "_1"); test("struct Foo { int x, y, z; }" "kprobe:f" "{" " @foo = *(struct Foo*)arg0;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_save_nested.cpp000066400000000000000000000006231477746507000225640ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_save_nested) { test("struct Foo { int m; struct { int x; int y; } bar; int n; }" "kprobe:f" "{" " @foo = *(struct Foo*)arg0;" " @bar = @foo.bar;" " @x = @foo.bar.x;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_save_string.cpp000066400000000000000000000005311477746507000226060ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_save_string) { test("struct Foo { char str[32]; }" "kprobe:f" "{" " @foo = *(struct Foo*)arg0;" " @str = @foo.str;" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_short.cpp000066400000000000000000000010111477746507000214130ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_short) { test("struct Foo { short x; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @x = $foo.x;" "}", std::string(NAME) + "_1"); test("struct Foo { short x; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @x = $foo->x;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_string_array.cpp000066400000000000000000000010461477746507000227700ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_string_array) { test("struct Foo { char str[32]; }" "kprobe:f" "{" " $foo = *(struct Foo*)arg0;" " @mystr = $foo.str;" "}", std::string(NAME) + "_1"); test("struct Foo { char str[32]; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @mystr = $foo->str;" "}", std::string(NAME) + "_2"); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/struct_string_ptr.cpp000066400000000000000000000005341477746507000224600ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, struct_string_ptr) { test("struct Foo { char *str; }" "kprobe:f" "{" " $foo = (struct Foo*)arg0;" " @mystr = str($foo->str);" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/subprog.cpp000066400000000000000000000004041477746507000203360ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, subprog_arguments) { test("fn add($a : int64, $b : int64): int64 { return $a + $b; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/ternary_buf.cpp000066400000000000000000000004001477746507000211710ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ternary_buf) { test("kprobe:f { $x = nsecs ? buf(\"hi\", 2) : buf(\"bye\", 3); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/ternary_int.cpp000066400000000000000000000003531477746507000212160ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ternary_int) { test("kprobe:f { @x = pid < 10000 ? 1 : 2; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/ternary_none.cpp000066400000000000000000000003741477746507000213660ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ternary_none) { test("kprobe:f { pid < 10000 ? printf(\"hello\") : exit(); }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/ternary_str.cpp000066400000000000000000000003651477746507000212370ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ternary_str) { test("kprobe:f { @x = pid < 10000 ? \"lo\" : \"hi\"; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/ternary_tuple.cpp000066400000000000000000000004161477746507000215550ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, ternary_tuple) { test("kprobe:f { $x = nsecs ? (\"hi\", 1) : (\"extralongstring\", 2) }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/tuple.cpp000066400000000000000000000011671477746507000200150ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, tuple) { test(R"_(k:f { @t = (1, 2, "str"); })_", NAME); } TEST(codegen, tuple_map_val_different_sizes) { test(R"_(k:f { @a = (1, "hi"); @a = (1, "hellolongstr"); })_", NAME); } TEST(codegen, tuple_variable_different_sizes) { test(R"_(k:f { $t = (1, "hi"); $t = (1, "hellolongstr"); })_", NAME); } TEST(codegen, nested_tuple_different_sizes) { test(R"_(k:f { $t = (1, ("hi", 3)); $t = (1, ("hellolongstr", 4)); })_", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/tuple_array_struct.cpp000066400000000000000000000005641477746507000226170ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, tuple_array_struct) { test("struct Foo { char c; int x; } struct Bar { int y[4]; }" "kprobe:f" "{" " @t = (*((struct Foo *)arg0), ((struct Bar *)arg1)->y);" "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/tuple_bytearray.cpp000066400000000000000000000004031477746507000220670ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, tuple_bytearray) { test(R"_(k:f { @t = ((uint8)1, usym(reg("ip")), 10); })_", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/unroll.cpp000066400000000000000000000003471477746507000201760ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, unroll) { test("BEGIN { @i = 0; unroll(5) { @i += 1 } }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/unroll_async_id.cpp000066400000000000000000000003641477746507000220460ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, unroll_async_id) { test(R"(BEGIN { $i = 0; unroll(5) { $i += 1; } })", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/variable.cpp000066400000000000000000000003601477746507000204430ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, variable) { test("kprobe:f { $var = comm; @x = $var; @y = $var }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/variable_assign_array.cpp000066400000000000000000000005401477746507000232050ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, variable_assign_array) { test("struct Foo { int arr[4]; }" "kprobe:f" "{" " $var = ((struct Foo *)arg0)->arr;" " @x = $var[0]; " "}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/variable_assign_string.cpp000066400000000000000000000006101477746507000233730ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, variable_assign_string) { test("kprobe:f { $var = \"blah\"; @map = $var; }", NAME); } TEST(codegen, variable_assign_string_shorter) { test("kprobe:f { $var = \"xxxxx\"; $var = \"a\"; @map = $var; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/variable_increment_decrement.cpp000066400000000000000000000007451477746507000245440ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { TEST(codegen, variable_pre_inc) { test("BEGIN { $x = 10; $y = ++$x; }", NAME); } TEST(codegen, variable_post_inc) { test("BEGIN { $x = 10; $y = $x++; }", NAME); } TEST(codegen, variable_pre_dec) { test("BEGIN { $x = 10; $y = --$x; }", NAME); } TEST(codegen, variable_post_dec) { test("BEGIN { $x = 10; $y = $x--; }", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/codegen/variable_map_key_lifetime.cpp000066400000000000000000000003351477746507000240300ustar00rootroot00000000000000#include "common.h" namespace bpftrace::test::codegen { TEST(codegen, variable_map_key_lifetime) { test(R"(BEGIN { $myvar = "abc"; @x[$myvar] = 1; @x[$myvar] = 1; })", NAME); } } // namespace bpftrace::test::codegen bpftrace-0.23.2/tests/codegen/while_loop_no_unroll.cpp000066400000000000000000000005461477746507000231140ustar00rootroot00000000000000#include "common.h" namespace bpftrace { namespace test { namespace codegen { // Trip counts under 10 are usually unrolled automatically. // // This tests that the loop isn't unrolled. TEST(codegen, while_loop_no_unroll) { test("i:s:1 { $a = 0; while ($a <= 10) { @=$a++; }}", NAME); } } // namespace codegen } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/collect_nodes.cpp000066400000000000000000000065441477746507000201010ustar00rootroot00000000000000#include "ast/passes/collect_nodes.h" #include "gtest/gtest.h" #include #include namespace bpftrace::test::collect_nodes { using namespace bpftrace::ast; template void test(const std::vector> &expected, const std::vector> &actual) { ASSERT_EQ(expected.size(), actual.size()); for (size_t i = 0; i < expected.size(); i++) { EXPECT_EQ(&expected[i].get(), &actual[i].get()); } } TEST(CollectNodes, direct) { ASTContext ctx; auto &var = *ctx.make_node("myvar", bpftrace::location{}); CollectNodes visitor(ctx); visitor.visit(var); test({ var }, visitor.nodes()); } TEST(CollectNodes, indirect) { ASTContext ctx; auto &var = *ctx.make_node("myvar", bpftrace::location{}); auto &unop = *ctx.make_node( Operator::INCREMENT, &var, false, bpftrace::location{}); CollectNodes visitor(ctx); visitor.visit(unop); test({ var }, visitor.nodes()); } TEST(CollectNodes, none) { ASTContext ctx; auto &map = *ctx.make_node("myvar", bpftrace::location{}); auto &unop = *ctx.make_node( Operator::INCREMENT, &map, false, bpftrace::location{}); CollectNodes visitor(ctx); visitor.visit(unop); test({}, visitor.nodes()); } TEST(CollectNodes, multiple_runs) { ASTContext ctx; auto &var1 = *ctx.make_node("myvar1", bpftrace::location{}); auto &unop1 = *ctx.make_node( Operator::INCREMENT, &var1, false, bpftrace::location{}); auto &var2 = *ctx.make_node("myvar2", bpftrace::location{}); auto &unop2 = *ctx.make_node( Operator::INCREMENT, &var2, false, bpftrace::location{}); CollectNodes visitor(ctx); visitor.visit(unop1); visitor.visit(unop2); test({ var1, var2 }, visitor.nodes()); } TEST(CollectNodes, multiple_children) { ASTContext ctx; auto &var1 = *ctx.make_node("myvar1", bpftrace::location{}); auto &var2 = *ctx.make_node("myvar2", bpftrace::location{}); auto &binop = *ctx.make_node( &var1, Operator::PLUS, &var2, bpftrace::location{}); CollectNodes visitor(ctx); visitor.visit(binop); test({ var1, var2 }, visitor.nodes()); } TEST(CollectNodes, predicate) { ASTContext ctx; auto &var1 = *ctx.make_node("myvar1", bpftrace::location{}); auto &var2 = *ctx.make_node("myvar2", bpftrace::location{}); auto &binop = *ctx.make_node( &var1, Operator::PLUS, &var2, bpftrace::location{}); CollectNodes visitor(ctx); visitor.visit(binop, [](const auto &var) { return var.ident == "myvar2"; }); test({ var2 }, visitor.nodes()); } TEST(CollectNodes, nested) { ASTContext ctx; auto &var1 = *ctx.make_node("myvar1", bpftrace::location{}); auto &var2 = *ctx.make_node("myvar2", bpftrace::location{}); auto &var3 = *ctx.make_node("myvar3", bpftrace::location{}); auto &binop1 = *ctx.make_node( &var1, Operator::PLUS, &var2, bpftrace::location{}); auto &binop2 = *ctx.make_node( &binop1, Operator::MINUS, &var3, bpftrace::location{}); CollectNodes visitor(ctx); visitor.visit(binop2, [](const auto &binop) { return binop.op == Operator::PLUS; }); test({ binop1 }, visitor.nodes()); } } // namespace bpftrace::test::collect_nodes bpftrace-0.23.2/tests/config.cpp000066400000000000000000000123051477746507000165210ustar00rootroot00000000000000#include "config.h" #include "mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include namespace bpftrace::test { TEST(Config, get_and_set) { auto config = Config(); auto config_setter = ConfigSetter(config, ConfigSource::env_var); // check all the keys EXPECT_TRUE(config_setter.set(ConfigKeyBool::cpp_demangle, true)); EXPECT_EQ(config.get(ConfigKeyBool::cpp_demangle), true); EXPECT_TRUE(config_setter.set(ConfigKeyBool::lazy_symbolication, true)); EXPECT_EQ(config.get(ConfigKeyBool::lazy_symbolication), true); EXPECT_TRUE(config_setter.set(ConfigKeyInt::log_size, 10)); EXPECT_EQ(config.get(ConfigKeyInt::log_size), 10); EXPECT_TRUE(config_setter.set(ConfigKeyInt::max_cat_bytes, 10)); EXPECT_EQ(config.get(ConfigKeyInt::max_cat_bytes), 10); EXPECT_TRUE(config_setter.set(ConfigKeyInt::max_map_keys, 10)); EXPECT_EQ(config.get(ConfigKeyInt::max_map_keys), 10); EXPECT_TRUE(config_setter.set(ConfigKeyInt::max_probes, 10)); EXPECT_EQ(config.get(ConfigKeyInt::max_probes), 10); EXPECT_TRUE(config_setter.set(ConfigKeyInt::max_bpf_progs, 10)); EXPECT_EQ(config.get(ConfigKeyInt::max_bpf_progs), 10); EXPECT_TRUE(config_setter.set(ConfigKeyInt::max_strlen, 10)); EXPECT_EQ(config.get(ConfigKeyInt::max_strlen), 10); EXPECT_TRUE(config_setter.set(ConfigKeyInt::max_type_res_iterations, 10)); EXPECT_EQ(config.get(ConfigKeyInt::max_type_res_iterations), 10); EXPECT_TRUE(config_setter.set(ConfigKeyInt::perf_rb_pages, 10)); EXPECT_EQ(config.get(ConfigKeyInt::perf_rb_pages), 10); EXPECT_TRUE(config_setter.set(ConfigKeyString::str_trunc_trailer, "str")); EXPECT_EQ(config.get(ConfigKeyString::str_trunc_trailer), "str"); EXPECT_TRUE(config_setter.set(StackMode::bpftrace)); EXPECT_EQ(config.get(ConfigKeyStackMode::default_), StackMode::bpftrace); // Test that this is also true by default, as a requirement. EXPECT_TRUE(config.get(ConfigKeyBool::print_maps_on_exit)); EXPECT_TRUE(config_setter.set(ConfigKeyBool::print_maps_on_exit, false)); EXPECT_EQ(config.get(ConfigKeyBool::print_maps_on_exit), false); EXPECT_TRUE(config_setter.set(UserSymbolCacheType::per_program)); EXPECT_EQ(config.get(ConfigKeyUserSymbolCacheType::default_), UserSymbolCacheType::per_program); EXPECT_TRUE(config_setter.set(ConfigMissingProbes::ignore)); EXPECT_EQ(config.get(ConfigKeyMissingProbes::default_), ConfigMissingProbes::ignore); } TEST(Config, get_config_key) { auto config = Config(); std::string err_msg; EXPECT_TRUE(config.get_config_key("log_size", err_msg).has_value()); EXPECT_TRUE(config.get_config_key("Log_Size", err_msg).has_value()); EXPECT_TRUE(config.get_config_key("bpftrace_log_sIze", err_msg).has_value()); EXPECT_TRUE(config.get_config_key("BPFTRACE_LOG_SIZE", err_msg).has_value()); // check the error message EXPECT_FALSE(config.get_config_key("logsize", err_msg).has_value()); EXPECT_EQ(err_msg, "Unrecognized config variable: logsize"); EXPECT_FALSE(config.get_config_key("max_ast_nodes", err_msg).has_value()); EXPECT_EQ(err_msg, "max_ast_nodes can only be set as an environment variable"); } TEST(ConfigSetter, set_stack_mode) { auto config = Config(); auto config_setter = ConfigSetter(config, ConfigSource::env_var); EXPECT_FALSE(config_setter.set_stack_mode("invalid")); EXPECT_TRUE(config_setter.set_stack_mode("raw")); EXPECT_EQ(config.get(ConfigKeyStackMode::default_), StackMode::raw); } TEST(ConfigSetter, set_user_symbol_cache_type) { auto config = Config(); auto config_setter = ConfigSetter(config, ConfigSource::env_var); EXPECT_FALSE(config_setter.set_user_symbol_cache_type("invalid")); EXPECT_TRUE(config_setter.set_user_symbol_cache_type("NONE")); EXPECT_EQ(config.get(ConfigKeyUserSymbolCacheType::default_), UserSymbolCacheType::none); } TEST(ConfigSetter, set_missing_probes) { auto config = Config(); auto config_setter = ConfigSetter(config, ConfigSource::script); EXPECT_EQ(config.get(ConfigKeyMissingProbes::default_), ConfigMissingProbes::warn); EXPECT_FALSE(config_setter.set_missing_probes_config("invalid")); EXPECT_TRUE(config_setter.set_missing_probes_config("error")); EXPECT_EQ(config.get(ConfigKeyMissingProbes::default_), ConfigMissingProbes::error); } TEST(ConfigSetter, source_precedence) { auto config = Config(); auto config_setter_env = ConfigSetter(config, ConfigSource::env_var); auto config_setter_script = ConfigSetter(config, ConfigSource::script); // env var takes precedence over script EXPECT_TRUE(config_setter_env.set(ConfigKeyInt::max_map_keys, 10)); EXPECT_FALSE(config_setter_script.set(ConfigKeyInt::max_map_keys, 11)); EXPECT_EQ(config.get(ConfigKeyInt::max_map_keys), 10); EXPECT_TRUE(config_setter_script.set(ConfigKeyInt::max_cat_bytes, 19)); EXPECT_TRUE(config_setter_env.set(ConfigKeyInt::max_cat_bytes, 20)); EXPECT_EQ(config.get(ConfigKeyInt::max_cat_bytes), 20); } TEST(ConfigSetter, same_source_cannot_set_twice) { auto config = Config(); auto config_setter = ConfigSetter(config, ConfigSource::env_var); EXPECT_TRUE(config_setter.set(ConfigKeyInt::max_map_keys, 10)); EXPECT_FALSE(config_setter.set(ConfigKeyInt::max_map_keys, 11)); } } // namespace bpftrace::test bpftrace-0.23.2/tests/config_analyser.cpp000066400000000000000000000113111477746507000204130ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include "ast/passes/config_analyser.h" #include "ast/passes/semantic_analyser.h" #include "clang_parser.h" #include "driver.h" #include "mocks.h" namespace bpftrace::test::config_analyser { using ::testing::_; void test(BPFtrace &bpftrace, const std::string &input, std::string_view expected_error, bool expected_result) { Driver driver(bpftrace); std::stringstream out; std::stringstream msg; msg << "\nInput:\n" << input << "\n\nOutput:\n"; ASSERT_EQ(driver.parse_str(input), 0); ClangParser clang; ASSERT_TRUE(clang.parse(driver.ctx.root, bpftrace)); ASSERT_EQ(driver.parse_str(input), 0); out.str(""); ast::SemanticAnalyser semantics(driver.ctx, bpftrace, out, false); ASSERT_EQ(semantics.analyse(), 0) << msg.str() << out.str(); ast::ConfigAnalyser config_analyser(driver.ctx, bpftrace, out); EXPECT_EQ(config_analyser.analyse(), expected_result) << msg.str() << out.str(); if (expected_error.data()) { if (!expected_error.empty() && expected_error[0] == '\n') expected_error.remove_prefix(1); // Remove initial '\n' EXPECT_EQ(out.str(), expected_error); } } void test(const std::string &input, bool expected_result) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, input, {}, expected_result); } void test(const std::string &input, std::string_view expected_error, bool expected_result) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, input, expected_error, expected_result); } void test(BPFtrace &bpftrace, const std::string &input) { test(bpftrace, input, {}, true); } TEST(config_analyser, config) { test("config = { BAD_CONFIG=1 } BEGIN { }", false); test("config = { BPFTRACE_MAX_MAP_KEYS=1 } BEGIN { }", true); test("config = { BPFTRACE_MAX_MAP_KEYS=perf } BEGIN { }", false); test("config = { BPFTRACE_STACK_MODE=perf } BEGIN { }", true); test("config = { stack_mode=perf } BEGIN { }", true); test("config = { BPFTRACE_MAX_MAP_KEYS=1; stack_mode=perf } BEGIN { $ns = " "nsecs(); }", true); test("config = { BPFTRACE_CACHE_USER_SYMBOLS=\"PER_PROGRAM\" } BEGIN { $ns = " "nsecs(); }", true); } TEST(config_analyser, config_error) { test("config = { BAD_CONFIG=1 } BEGIN { }", R"(stdin:1:12-23: ERROR: Unrecognized config variable: BAD_CONFIG config = { BAD_CONFIG=1 } BEGIN { } ~~~~~~~~~~~ )", false); test( "config = { BPFTRACE_MAX_PROBES=\"hello\" } BEGIN { }", R"(stdin:1:12-32: ERROR: Invalid type for BPFTRACE_MAX_PROBES. Type: string. Expected Type: int config = { BPFTRACE_MAX_PROBES="hello" } BEGIN { } ~~~~~~~~~~~~~~~~~~~~ )", false); test( "config = { max_ast_nodes=1 } BEGIN { }", R"(stdin:1:12-26: ERROR: max_ast_nodes can only be set as an environment variable config = { max_ast_nodes=1 } BEGIN { } ~~~~~~~~~~~~~~ )", false); } TEST(config_analyser, config_setting) { auto bpftrace = get_mock_bpftrace(); EXPECT_NE(bpftrace->config_.get(ConfigKeyInt::max_map_keys), 9); test(*bpftrace, "config = { BPFTRACE_MAX_MAP_KEYS=9 } BEGIN { }"); EXPECT_EQ(bpftrace->config_.get(ConfigKeyInt::max_map_keys), 9); EXPECT_NE(bpftrace->config_.get(ConfigKeyStackMode::default_), StackMode::perf); test(*bpftrace, "config = { stack_mode=perf } BEGIN { }"); EXPECT_EQ(bpftrace->config_.get(ConfigKeyStackMode::default_), StackMode::perf); EXPECT_NE(bpftrace->config_.get(ConfigKeyUserSymbolCacheType::default_), UserSymbolCacheType::per_program); EXPECT_NE(bpftrace->config_.get(ConfigKeyInt::log_size), 150); test(*bpftrace, "config = { BPFTRACE_CACHE_USER_SYMBOLS=\"PER_PROGRAM\"; log_size=150 " "} BEGIN { }"); EXPECT_EQ(bpftrace->config_.get(ConfigKeyUserSymbolCacheType::default_), UserSymbolCacheType::per_program); EXPECT_EQ(bpftrace->config_.get(ConfigKeyInt::log_size), 150); // When liblldb is present, the default config for symbol_source is "dwarf", // otherwise the default is "symbol_table". #ifdef HAVE_LIBLLDB EXPECT_EQ(bpftrace->config_.get(ConfigKeySymbolSource::default_), ConfigSymbolSource::dwarf); test(*bpftrace, "config = { symbol_source = \"symbol_table\" } BEGIN { }"); EXPECT_EQ(bpftrace->config_.get(ConfigKeySymbolSource::default_), ConfigSymbolSource::symbol_table); #else EXPECT_EQ(bpftrace->config_.get(ConfigKeySymbolSource::default_), ConfigSymbolSource::symbol_table); test(*bpftrace, "config = { symbol_source = \"dwarf\" } BEGIN { }"); EXPECT_EQ(bpftrace->config_.get(ConfigKeySymbolSource::default_), ConfigSymbolSource::dwarf); #endif } } // namespace bpftrace::test::config_analyser bpftrace-0.23.2/tests/cstring_view.cpp000066400000000000000000000022001477746507000177500ustar00rootroot00000000000000#include "container/cstring_view.h" #include "gtest/gtest.h" #include namespace bpftrace::test::cstring_view { using bpftrace::cstring_view; TEST(cstring_view, c_string) { const char *str = "abc"; cstring_view sv{ str }; EXPECT_EQ("abc", sv); EXPECT_EQ('a', sv[0]); EXPECT_EQ('b', sv[1]); EXPECT_EQ('c', sv[2]); EXPECT_EQ('\0', sv[3]); } TEST(cstring_view, std_string) { std::string str = "abc"; cstring_view sv{ str }; EXPECT_EQ("abc", sv); EXPECT_EQ('a', sv[0]); EXPECT_EQ('b', sv[1]); EXPECT_EQ('c', sv[2]); EXPECT_EQ('\0', sv[3]); } TEST(cstring_view, std_string_view) { EXPECT_FALSE((std::is_constructible_v)); // Sanity checks: EXPECT_TRUE((std::is_constructible_v)); EXPECT_TRUE((std::is_constructible_v)); } TEST(cstring_view, length) { cstring_view sv{ "abc" }; EXPECT_EQ("abc", sv); EXPECT_EQ(3, sv.size()); EXPECT_EQ(3, sv.length()); } TEST(cstring_view, c_str) { cstring_view sv{ "abc" }; EXPECT_EQ(0, strcmp(sv.c_str(), "abc")); } } // namespace bpftrace::test::cstring_view bpftrace-0.23.2/tests/data/000077500000000000000000000000001477746507000154605ustar00rootroot00000000000000bpftrace-0.23.2/tests/data/CMakeLists.txt000066400000000000000000000111221477746507000202150ustar00rootroot00000000000000# Generates header files that store debuginfo from data_source.c in the form # of byte arrays find_program(XXD xxd REQUIRED) find_program(PAHOLE pahole REQUIRED) find_program(LLVM_OBJCOPY NAMES llvm-objcopy llvm-objcopy-${LLVM_VERSION_MAJOR} llvm${LLVM_VERSION_MAJOR}-objcopy REQUIRED) find_program(NM nm REQUIRED) find_program(AWK awk REQUIRED) find_program(STRIP strip REQUIRED) # Build data_source.o and inject BTF into it set(DATA_SOURCE_C ${CMAKE_CURRENT_SOURCE_DIR}/data_source.c) set(DATA_SOURCE_O ${CMAKE_CURRENT_BINARY_DIR}/data_source.o) set(DATA_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/data_source) add_custom_command( OUTPUT ${DATA_SOURCE_O} COMMAND gcc -g -c -o ${DATA_SOURCE_O} ${DATA_SOURCE_C} # pahole uses LLVM_OBJCOPY env var. # We must hack it like this b/c cmake does not support setting env vars at build time COMMAND bash -c "LLVM_OBJCOPY=${LLVM_OBJCOPY} pahole -J ${DATA_SOURCE_O}" DEPENDS ${DATA_SOURCE_C}) # We don't want to use just ${DATA_SOURCE_O} as the dependency of the below # commands as that would make data_source.o be regenerated for every command # which is not only inefficient but also prone to race conditions. # So, we introduce a custom target data_source_o but unfortunately, it is not # sufficient to use solely that either as it just creates a target-ordering # dependency and not a file dependency, which causes the below commands not be # rerun when data_source.o changes. # The solution here is to use **both** data_source_o (to ensure correct target # ordering) and ${DATA_SOURCE_O} (to create file dependency) targets. We tie # them together in the ${DATA_SOURCE_DEPS} variable which should be used. add_custom_target(data_source_o DEPENDS ${DATA_SOURCE_O}) set(DATA_SOURCE_DEPS data_source_o ${DATA_SOURCE_O}) # Generate btf_data from BTF in data_source.o set(BTF_DATA_FILE ${CMAKE_CURRENT_BINARY_DIR}/btf_data) add_custom_command( OUTPUT ${BTF_DATA_FILE} COMMAND ${LLVM_OBJCOPY} --dump-section .BTF=${BTF_DATA_FILE} ${DATA_SOURCE_O} DEPENDS ${DATA_SOURCE_DEPS}) # Generate btf_data.hex from btf_data set(BTF_DATA_HEX ${CMAKE_CURRENT_BINARY_DIR}/btf_data.hex) add_custom_command( OUTPUT ${BTF_DATA_HEX} COMMAND xxd -i < ${BTF_DATA_FILE} > ${BTF_DATA_HEX} DEPENDS ${BTF_DATA_FILE}) # Generate func_list.hex from data_source.o set(FUNC_LIST_HEX ${CMAKE_CURRENT_BINARY_DIR}/func_list.hex) add_custom_command( OUTPUT ${FUNC_LIST_HEX} COMMAND nm ${DATA_SOURCE_O} | awk -v ORS=\\\\n "$2 == \"T\" { print $3 }" > ${FUNC_LIST_HEX} VERBATIM DEPENDS ${DATA_SOURCE_DEPS}) if(${LLDB_FOUND}) # Generate dwarf_data from data_source.o set(DWARF_DATA_FILE ${CMAKE_CURRENT_BINARY_DIR}/dwarf_data) add_custom_command( OUTPUT ${DWARF_DATA_FILE} COMMAND gcc ${DATA_SOURCE_O} -o ${DATA_SOURCE} COMMAND strip --only-keep-debug -o ${DWARF_DATA_FILE} ${DATA_SOURCE} DEPENDS ${DATA_SOURCE_DEPS}) # Generate dwarf_data.hex from dwarf_data set(DWARF_DATA_HEX ${CMAKE_CURRENT_BINARY_DIR}/dwarf_data.hex) add_custom_command( OUTPUT ${DWARF_DATA_HEX} COMMAND xxd -i < ${DWARF_DATA_FILE} > ${DWARF_DATA_HEX} DEPENDS ${DWARF_DATA_FILE}) set(CONFIGURE_DWARF_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/configure_dwarf_headers.cmake) set(DWARF_DATA_H_IN ${CMAKE_CURRENT_SOURCE_DIR}/dwarf_data.h.in) set(DWARF_DATA_H ${CMAKE_CURRENT_BINARY_DIR}/dwarf_data.h) add_custom_command( OUTPUT ${DWARF_DATA_H} COMMAND ${CMAKE_COMMAND} -DFUNC_LIST_HEX=${FUNC_LIST_HEX} -DDWARF_DATA_HEX=${DWARF_DATA_HEX} -DDWARF_DATA_H_IN=${DWARF_DATA_H_IN} -DDWARF_DATA_H=${DWARF_DATA_H} -DDWARF_DATA_CXX_PATH=${CMAKE_CURRENT_BINARY_DIR}/data_source_cxx -P ${CONFIGURE_DWARF_HEADERS} DEPENDS ${DWARF_DATA_H_IN} ${FUNC_LIST_HEX} ${DWARF_DATA_HEX} ${CONFIGURE_DWARF_HEADERS}) add_custom_target(debuginfo_dwarf_data DEPENDS ${DWARF_DATA_H}) endif() set(CONFIGURE_BTF_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/configure_btf_headers.cmake) set(BTF_DATA_H_IN ${CMAKE_CURRENT_SOURCE_DIR}/btf_data.h.in) set(BTF_DATA_H ${CMAKE_CURRENT_BINARY_DIR}/btf_data.h) add_custom_command( OUTPUT ${BTF_DATA_H} COMMAND ${CMAKE_COMMAND} -DBTF_DATA_HEX=${BTF_DATA_HEX} -DFUNC_LIST_HEX=${FUNC_LIST_HEX} -DBTF_DATA_H_IN=${BTF_DATA_H_IN} -DBTF_DATA_H=${BTF_DATA_H} -P ${CONFIGURE_BTF_HEADERS} DEPENDS ${BTF_DATA_H_IN} ${BTF_DATA_HEX} ${FUNC_LIST_HEX} ${CONFIGURE_BTF_HEADERS}) add_custom_target(debuginfo_btf_data DEPENDS ${BTF_DATA_H}) # BTF doesn't support C++, so we only generate a data_source_cxx executable # to run the field_analyser tests on. add_executable(data_source_cxx data_source_cxx.cpp) target_compile_options(data_source_cxx PRIVATE -g) bpftrace-0.23.2/tests/data/btf_data.h.in000066400000000000000000000003621477746507000200030ustar00rootroot00000000000000#pragma once unsigned char btf_data[] = { ${BTF_DATA} }; unsigned int btf_data_len = sizeof(btf_data) / sizeof(btf_data[0]); unsigned char func_list[] = "${FUNC_LIST}"; unsigned int func_list_len = sizeof(func_list) / sizeof(func_list[0]); bpftrace-0.23.2/tests/data/configure_btf_headers.cmake000066400000000000000000000005141477746507000227710ustar00rootroot00000000000000# This logic needs to be in a separate cmake script b/c file() runs # at cmake configuration stage and _not_ during build. So this script # is wrapped in a custom command so that it's only run when necessary. file(READ ${BTF_DATA_HEX} BTF_DATA) file(READ ${FUNC_LIST_HEX} FUNC_LIST) configure_file(${BTF_DATA_H_IN} ${BTF_DATA_H}) bpftrace-0.23.2/tests/data/configure_dwarf_headers.cmake000066400000000000000000000004561477746507000233260ustar00rootroot00000000000000# This logic needs to be in a separate cmake script b/c file() runs # at cmake configuration stage and _not_ during build. So this script # is wrapped in a custom command so that it's only run when necessary. file(READ ${DWARF_DATA_HEX} DWARF_DATA) configure_file(${DWARF_DATA_H_IN} ${DWARF_DATA_H}) bpftrace-0.23.2/tests/data/data_source.c000066400000000000000000000044601477746507000201210ustar00rootroot00000000000000struct Foo1 { int a; char b; long c; }; struct Foo2 { int a; union { struct Foo1 f; struct { char g; }; }; }; struct Foo3 { struct Foo1 *foo1; const volatile struct Foo2 *restrict foo2; }; struct Foo3 foo3; struct Foo3 *func_1(int a, struct Foo1 *foo1, struct Foo2 *foo2, struct Foo3 *foo3) { return 0; } struct Foo3 *func_2(int a, int *b, struct Foo1 *foo1) { return 0; } // __attribute__((noinline)) is needed due to a LLDB/GCC compatibility bug struct Foo3 *__attribute__((noinline)) func_3(int a, int *b, struct Foo1 *foo1) { return 0; } struct FirstFieldsAreAnonUnion { union { int a; int b; }; int c; }; struct FirstFieldsAreAnonUnion first_fields_anon_union; struct Arrays { int int_arr[4]; char char_arr[8]; void *ptr_arr[2]; int multi_dim[3][2]; int zero[0]; int flexible[]; }; struct Arrays arrays; struct Arrays *func_arrays(struct Arrays *arr) { return 0; } struct ArrayWithCompoundData { struct Foo3 *data[2]; }; void func_array_with_compound_data(struct ArrayWithCompoundData *arr) { } struct task_struct { int pid; int pgid; int : 12; // padding int a : 8; int b : 1; int c : 3; int d : 20; }; struct file { int ino; }; struct vm_area_struct { unsigned long vm_start; unsigned long vm_end; }; struct bpf_iter__task { struct task_struct *task; }; struct bpf_iter__task_file { struct task_struct *task; struct file *file; }; struct bpf_iter__task_vma { struct task_struct *task; struct vm_area_struct *vma; }; int bpf_iter_task() { return 0; } int bpf_iter_task_file() { return 0; } int bpf_iter_task_vma() { return 0; } // kfunc definitions struct bpf_map {}; long bpf_map_sum_elem_count(const struct bpf_map *map) { return 0; } // kernel percpu variables __attribute__((section(".data..percpu"))) unsigned long process_counts; // Make sure all new mocked kernel functions are called in this main (below) // so they don't get optimzed away int main(void) { struct bpf_iter__task iter_task; struct bpf_iter__task_file iter_task_file; struct bpf_iter__task_vma iter_task_vma; struct bpf_map bpf_map; func_1(0, 0, 0, 0); bpf_iter_task(); bpf_iter_task_file(); bpf_iter_task_vma(); bpf_map_sum_elem_count(&bpf_map); return 0; } bpftrace-0.23.2/tests/data/data_source_cxx.cpp000066400000000000000000000030411477746507000213350ustar00rootroot00000000000000class Parent { private: int a; protected: int b; public: int c; int d; // Shadowed by Child::d, but should be reachable with a cast Parent(int a, int b, int c, int d) : a(a), b(b), c(c), d(d) { } }; class Child : public Parent { public: int d; int e; int f; Child(int a, int b, int c, int d, int e, int f) : Parent(a, b, c, d), d(d + 1), e(e), f(f) { } }; class GrandChild : public Child { public: int g; GrandChild(int a, int b, int c, int d, int e, int f, int g) : Child(a, b, c, d, e, f), g(g) { } }; struct Top { int x; }; struct Left : public Top { int y; }; struct Right : public Top { int z; }; struct Bottom : public Left, public Right { int w; }; struct Multi : public Parent, public Top { int abc; int &rabc; Multi(int a, int b, int c, int d, int e) : Parent{ a, b, c, d }, Top{ e }, abc{ e + 1 }, rabc{ abc } { } }; int func_1(Child &c, Parent &p __attribute__((unused))) { return dynamic_cast(c).d; } int func_2(GrandChild &lc) { return dynamic_cast(lc).d; } int func_3(Multi &m, Bottom &b __attribute__((unused))) { return m.abc; } int main() { Parent p{ 1, 2, 3, 4 }; Child c{ 1, 2, 3, 4, 5, 6 }; func_1(c, p); GrandChild lc{ 1, 2, 3, 4, 5, 6, 7 }; func_2(lc); Multi m{ 1, 2, 3, 4, 5 }; Bottom b{ { // Left { 1 }, // Left's Top 2 // Left's y }, { // Right { 3 }, // Right's Top 4 // Right's z }, 5 // Bottom's w }; func_3(m, b); return 0; } bpftrace-0.23.2/tests/data/dwarf_data.h.in000066400000000000000000000003701477746507000203320ustar00rootroot00000000000000#pragma once constexpr inline unsigned char dwarf_data[] = { ${DWARF_DATA} }; constexpr inline unsigned int dwarf_data_len = sizeof(dwarf_data) / sizeof(dwarf_data[0]); constexpr inline const char *dwarf_data_cxx_path = "${DWARF_DATA_CXX_PATH}"; bpftrace-0.23.2/tests/dwarf_common.h000066400000000000000000000016071477746507000173770ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include "data/dwarf_data.h" class test_dwarf : public ::testing::Test { protected: static void SetUpTestSuite() { std::ofstream file(bin_, std::ios::trunc | std::ios::binary); file.write(reinterpret_cast(dwarf_data), dwarf_data_len); file.close(); if (!file) throw std::runtime_error("Failed to create dwarf data file"); // Give executable permissions to everyone int err = chmod(bin_, 0755); if (err) throw std::runtime_error("Failed to chmod dwarf data file: " + std::to_string(err)); } static void TearDownTestSuite() { std::remove(bin_); } static constexpr const char *bin_ = "/tmp/bpftrace-test-dwarf-data"; static constexpr const char *cxx_bin_ = dwarf_data_cxx_path; }; bpftrace-0.23.2/tests/field_analyser.cpp000066400000000000000000000722121477746507000202400ustar00rootroot00000000000000#include "ast/passes/field_analyser.h" #include "driver.h" #include "mocks.h" #include "gtest/gtest.h" namespace bpftrace::test::field_analyser { #include "btf_common.h" using ::testing::_; void test(BPFtrace &bpftrace, const std::string &input, int expected_result = 0) { std::stringstream out; std::stringstream msg; msg << "\nInput:\n" << input << "\n\nOutput:\n"; Driver driver(bpftrace); EXPECT_EQ(driver.parse_str(input), 0); ast::FieldAnalyser fields(driver.ctx, bpftrace, out); EXPECT_EQ(fields.analyse(), expected_result) << msg.str() + out.str(); } void test(const std::string &input, int expected_result = 0) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, input, expected_result); } class field_analyser_btf : public test_btf {}; TEST_F(field_analyser_btf, fentry_args) { // func_1 and func_2 have different args, but none of them // is used in probe code, so we're good -> PASS test("fentry:func_1, fentry:func_2 { }", 0); // func_1 and func_2 have different args, one of them // is used in probe code, we can't continue -> FAIL test("fentry:func_1, fentry:func_2 { $x = args.foo; }", 1); // func_2 and func_3 have same args -> PASS test("fentry:func_2, fentry:func_3 { }", 0); // func_2 and func_3 have same args -> PASS test("fentry:func_2, fentry:func_3 { $x = args.foo1; }", 0); // aaa does not exist -> FAIL test("fentry:func_2, fentry:aaa { $x = args.foo1; }", 1); // func_* have different args, but none of them // is used in probe code, so we're good -> PASS test("fentry:func_* { }", 0); // func_* have different args, one of them // is used in probe code, we can't continue -> FAIL test("fentry:func_* { $x = args.foo1; }", 1); } TEST_F(field_analyser_btf, btf_types) { BPFtrace bpftrace; bpftrace.parse_btf({}); test(bpftrace, "kprobe:sys_read {\n" " @x1 = (struct Foo1 *) curtask;\n" " @x2 = (struct Foo2 *) curtask;\n" " @x3 = (struct Foo3 *) curtask;\n" "}", 0); ASSERT_TRUE(bpftrace.structs.Has("struct Foo1")); ASSERT_TRUE(bpftrace.structs.Has("struct Foo2")); ASSERT_TRUE(bpftrace.structs.Has("struct Foo3")); auto foo1 = bpftrace.structs.Lookup("struct Foo1").lock(); auto foo2 = bpftrace.structs.Lookup("struct Foo2").lock(); auto foo3 = bpftrace.structs.Lookup("struct Foo3").lock(); EXPECT_EQ(foo1->size, 16); ASSERT_EQ(foo1->fields.size(), 3U); ASSERT_TRUE(foo1->HasField("a")); ASSERT_TRUE(foo1->HasField("b")); ASSERT_TRUE(foo1->HasField("c")); EXPECT_TRUE(foo1->GetField("a").type.IsIntTy()); EXPECT_EQ(foo1->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo1->GetField("a").offset, 0); EXPECT_TRUE(foo1->GetField("b").type.IsIntTy()); EXPECT_EQ(foo1->GetField("b").type.GetSize(), 1U); EXPECT_EQ(foo1->GetField("b").offset, 4); EXPECT_TRUE(foo1->GetField("c").type.IsIntTy()); EXPECT_EQ(foo1->GetField("c").type.GetSize(), 8U); EXPECT_EQ(foo1->GetField("c").offset, 8); EXPECT_EQ(foo2->size, 24); ASSERT_EQ(foo2->fields.size(), 3U); ASSERT_TRUE(foo2->HasField("a")); ASSERT_TRUE(foo2->HasField("f")); ASSERT_TRUE(foo2->HasField("g")); EXPECT_TRUE(foo2->GetField("a").type.IsIntTy()); EXPECT_EQ(foo2->GetField("a").type.GetSize(), 4U); EXPECT_EQ(foo2->GetField("a").offset, 0); EXPECT_TRUE(foo2->GetField("f").type.IsRecordTy()); EXPECT_EQ(foo2->GetField("f").type.GetSize(), 16U); EXPECT_EQ(foo2->GetField("f").offset, 8); EXPECT_TRUE(foo2->GetField("g").type.IsIntTy()); EXPECT_EQ(foo2->GetField("g").type.GetSize(), 1U); EXPECT_EQ(foo2->GetField("g").offset, 8); EXPECT_EQ(foo3->size, 16); ASSERT_EQ(foo3->fields.size(), 2U); ASSERT_TRUE(foo3->HasField("foo1")); ASSERT_TRUE(foo3->HasField("foo2")); auto foo1_field = foo3->GetField("foo1"); auto foo2_field = foo3->GetField("foo2"); EXPECT_TRUE(foo1_field.type.IsPtrTy()); EXPECT_EQ(foo1_field.type.GetPointeeTy()->GetName(), "struct Foo1"); EXPECT_EQ(foo1_field.offset, 0); EXPECT_TRUE(foo2_field.type.IsPtrTy()); EXPECT_EQ(foo2_field.type.GetPointeeTy()->GetName(), "struct Foo2"); EXPECT_EQ(foo2_field.offset, 8); } TEST_F(field_analyser_btf, btf_arrays) { BPFtrace bpftrace; bpftrace.parse_btf({}); test(bpftrace, "BEGIN {\n" " @ = (struct Arrays *) 0;\n" "}", 0); ASSERT_TRUE(bpftrace.structs.Has("struct Arrays")); auto arrs = bpftrace.structs.Lookup("struct Arrays").lock(); EXPECT_EQ(arrs->size, 64); ASSERT_EQ(arrs->fields.size(), 6U); ASSERT_TRUE(arrs->HasField("int_arr")); ASSERT_TRUE(arrs->HasField("char_arr")); ASSERT_TRUE(arrs->HasField("ptr_arr")); ASSERT_TRUE(arrs->HasField("multi_dim")); ASSERT_TRUE(arrs->HasField("zero")); ASSERT_TRUE(arrs->HasField("flexible")); EXPECT_TRUE(arrs->GetField("int_arr").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("int_arr").type.GetNumElements(), 4); EXPECT_TRUE(arrs->GetField("int_arr").type.GetElementTy()->IsIntTy()); EXPECT_EQ(arrs->GetField("int_arr").type.GetSize(), 16U); EXPECT_EQ(arrs->GetField("int_arr").offset, 0); EXPECT_TRUE(arrs->GetField("char_arr").type.IsStringTy()); EXPECT_EQ(arrs->GetField("char_arr").type.GetSize(), 8U); EXPECT_EQ(arrs->GetField("char_arr").offset, 16); EXPECT_TRUE(arrs->GetField("ptr_arr").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("ptr_arr").type.GetNumElements(), 2); EXPECT_TRUE(arrs->GetField("ptr_arr").type.GetElementTy()->IsPtrTy()); EXPECT_EQ(arrs->GetField("ptr_arr").type.GetSize(), 2 * sizeof(uintptr_t)); EXPECT_EQ(arrs->GetField("ptr_arr").offset, 24); // BTF flattens multi-dimensional arrays, so this test doesn't // check the correct number of elements. The correct values are // below in 'field_analyser_btf.btf_arrays_multi_dim'. EXPECT_TRUE(arrs->GetField("multi_dim").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("multi_dim").type.GetNumElements(), 6); EXPECT_TRUE(arrs->GetField("multi_dim").type.GetElementTy()->IsIntTy()); EXPECT_EQ(arrs->GetField("multi_dim").type.GetSize(), 24U); EXPECT_EQ(arrs->GetField("multi_dim").offset, 40); EXPECT_TRUE(arrs->GetField("zero").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("zero").type.GetNumElements(), 0); EXPECT_TRUE(arrs->GetField("zero").type.GetElementTy()->IsIntTy()); EXPECT_EQ(arrs->GetField("zero").type.GetSize(), 0U); EXPECT_EQ(arrs->GetField("zero").offset, 64); EXPECT_TRUE(arrs->GetField("flexible").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("flexible").type.GetNumElements(), 0); EXPECT_TRUE(arrs->GetField("flexible").type.GetElementTy()->IsIntTy()); EXPECT_EQ(arrs->GetField("flexible").type.GetSize(), 0U); EXPECT_EQ(arrs->GetField("flexible").offset, 64); } TEST_F(field_analyser_btf, btf_arrays_multi_dim) { GTEST_SKIP() << "BTF flattens multi-dimensional arrays #3082"; BPFtrace bpftrace; bpftrace.parse_btf({}); test(bpftrace, "BEGIN {\n" " @ = (struct Arrays *) 0;\n" "}", 0); ASSERT_TRUE(bpftrace.structs.Has("struct Arrays")); auto arrs = bpftrace.structs.Lookup("struct Arrays").lock(); ASSERT_TRUE(arrs->HasField("multi_dim")); EXPECT_TRUE(arrs->GetField("multi_dim").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("multi_dim").offset, 40); EXPECT_EQ(arrs->GetField("multi_dim").type.GetSize(), 24U); EXPECT_EQ(arrs->GetField("multi_dim").type.GetNumElements(), 3); EXPECT_TRUE(arrs->GetField("multi_dim").type.GetElementTy()->IsArrayTy()); EXPECT_EQ(arrs->GetField("multi_dim").type.GetElementTy()->GetSize(), 8U); EXPECT_EQ(arrs->GetField("multi_dim").type.GetElementTy()->GetNumElements(), 2); EXPECT_TRUE(arrs->GetField("multi_dim") .type.GetElementTy() ->GetElementTy() ->IsIntTy()); EXPECT_EQ(arrs->GetField("multi_dim") .type.GetElementTy() ->GetElementTy() ->GetSize(), 4U); } void test_arrays_compound_data(BPFtrace &bpftrace) { ASSERT_TRUE(bpftrace.structs.Has("struct ArrayWithCompoundData")); auto arrs = bpftrace.structs.Lookup("struct ArrayWithCompoundData").lock(); EXPECT_EQ(arrs->size, 2 * sizeof(uintptr_t)); ASSERT_EQ(arrs->fields.size(), 1U); ASSERT_TRUE(arrs->HasField("data")); auto &data_type = arrs->GetField("data").type; EXPECT_TRUE(data_type.IsArrayTy()); EXPECT_EQ(data_type.GetNumElements(), 2); EXPECT_EQ(data_type.GetSize(), 2 * sizeof(uintptr_t)); // Check that referenced types n-levels deep are all parsed from BTF auto &foo3_ptr_type = *data_type.GetElementTy(); ASSERT_TRUE(foo3_ptr_type.IsPtrTy()); auto &foo3_type = *foo3_ptr_type.GetPointeeTy(); ASSERT_TRUE(foo3_type.IsRecordTy()); ASSERT_TRUE(foo3_type.HasField("foo1")); auto &foo1_ptr_type = foo3_type.GetField("foo1").type; ASSERT_TRUE(foo1_ptr_type.IsPtrTy()); auto &foo1_type = *foo1_ptr_type.GetPointeeTy(); ASSERT_TRUE(foo1_type.IsRecordTy()); ASSERT_TRUE(foo1_type.HasField("a")); } TEST_F(field_analyser_btf, arrays_compound_data) { BPFtrace bpftrace; bpftrace.parse_btf({}); test(bpftrace, "BEGIN {\n" " $x = (struct ArrayWithCompoundData *) 0;\n" " $x->data[0]->foo1->a\n" "}", 0); test_arrays_compound_data(bpftrace); } TEST_F(field_analyser_btf, btf_types_struct_ptr) { BPFtrace bpftrace; bpftrace.parse_btf({}); test(bpftrace, "kprobe:sys_read {\n" " @x1 = ((struct Foo3 *) curtask);\n" " @x3 = @x1->foo2;\n" "}", 0); // @x1->foo2 should do 2 things: // - add struct Foo2 (without resolving its fields) // - resolve fields of struct Foo3 ASSERT_TRUE(bpftrace.structs.Has("struct Foo2")); ASSERT_TRUE(bpftrace.structs.Has("struct Foo3")); auto foo2 = bpftrace.structs.Lookup("struct Foo2").lock(); auto foo3 = bpftrace.structs.Lookup("struct Foo3").lock(); EXPECT_EQ(foo2->size, 24); ASSERT_EQ(foo2->fields.size(), 0U); // fields are not resolved EXPECT_EQ(foo3->size, 16); ASSERT_EQ(foo3->fields.size(), 2U); // fields are resolved } TEST_F(field_analyser_btf, btf_types_arr_access) { BPFtrace bpftrace; bpftrace.parse_btf({}); test(bpftrace, "fentry:func_1 {\n" " @foo2 = args.foo3[0].foo2;\n" "}", 0); // args.foo3[0].foo2 should do 2 things: // - add struct Foo2 (without resolving its fields) // - resolve fields of struct Foo3 ASSERT_TRUE(bpftrace.structs.Has("struct Foo2")); ASSERT_TRUE(bpftrace.structs.Has("struct Foo3")); auto foo2 = bpftrace.structs.Lookup("struct Foo2").lock(); auto foo3 = bpftrace.structs.Lookup("struct Foo3").lock(); EXPECT_EQ(foo2->size, 24); ASSERT_EQ(foo2->fields.size(), 0U); // fields are not resolved EXPECT_EQ(foo3->size, 16); ASSERT_EQ(foo3->fields.size(), 2U); // fields are resolved } TEST_F(field_analyser_btf, btf_types_bitfields) { BPFtrace bpftrace; bpftrace.parse_btf({}); test(bpftrace, "kprobe:sys_read { @ = curtask->pid; }"); ASSERT_TRUE(bpftrace.structs.Has("struct task_struct")); auto task_struct = bpftrace.structs.Lookup("struct task_struct").lock(); // clang-tidy doesn't seem to acknowledge that ASSERT_*() will // return from function so that these are in fact checked accesses. // // NOLINTBEGIN(bugprone-unchecked-optional-access) ASSERT_TRUE(task_struct->HasField("a")); EXPECT_TRUE(task_struct->GetField("a").type.IsIntTy()); EXPECT_EQ(task_struct->GetField("a").type.GetSize(), 4U); EXPECT_EQ(task_struct->GetField("a").offset, 9); ASSERT_TRUE(task_struct->GetField("a").bitfield.has_value()); EXPECT_EQ(task_struct->GetField("a").bitfield->read_bytes, 0x2U); EXPECT_EQ(task_struct->GetField("a").bitfield->access_rshift, 4U); EXPECT_EQ(task_struct->GetField("a").bitfield->mask, 0xFFU); ASSERT_TRUE(task_struct->HasField("b")); EXPECT_TRUE(task_struct->GetField("b").type.IsIntTy()); EXPECT_EQ(task_struct->GetField("b").type.GetSize(), 4U); EXPECT_EQ(task_struct->GetField("b").offset, 10); ASSERT_TRUE(task_struct->GetField("b").bitfield.has_value()); EXPECT_EQ(task_struct->GetField("b").bitfield->read_bytes, 0x1U); EXPECT_EQ(task_struct->GetField("b").bitfield->access_rshift, 4U); EXPECT_EQ(task_struct->GetField("b").bitfield->mask, 0x1U); ASSERT_TRUE(task_struct->HasField("c")); EXPECT_TRUE(task_struct->GetField("c").type.IsIntTy()); EXPECT_EQ(task_struct->GetField("c").type.GetSize(), 4U); EXPECT_EQ(task_struct->GetField("c").offset, 10); ASSERT_TRUE(task_struct->GetField("c").bitfield.has_value()); EXPECT_EQ(task_struct->GetField("c").bitfield->read_bytes, 0x1U); EXPECT_EQ(task_struct->GetField("c").bitfield->access_rshift, 5U); EXPECT_EQ(task_struct->GetField("c").bitfield->mask, 0x7U); ASSERT_TRUE(task_struct->HasField("d")); EXPECT_TRUE(task_struct->GetField("d").type.IsIntTy()); EXPECT_EQ(task_struct->GetField("d").type.GetSize(), 4U); EXPECT_EQ(task_struct->GetField("d").offset, 12); ASSERT_TRUE(task_struct->GetField("d").bitfield.has_value()); EXPECT_EQ(task_struct->GetField("d").bitfield->read_bytes, 0x3U); EXPECT_EQ(task_struct->GetField("d").bitfield->access_rshift, 0U); EXPECT_EQ(task_struct->GetField("d").bitfield->mask, 0xFFFFFU); // NOLINTEND(bugprone-unchecked-optional-access) } TEST_F(field_analyser_btf, btf_anon_union_first_in_struct) { BPFtrace bpftrace; bpftrace.parse_btf({}); test(bpftrace, "BEGIN { @ = (struct FirstFieldsAreAnonUnion *)0; }"); ASSERT_TRUE(bpftrace.structs.Has("struct FirstFieldsAreAnonUnion")); auto record = bpftrace.structs.Lookup("struct FirstFieldsAreAnonUnion").lock(); ASSERT_TRUE(record->HasField("a")); EXPECT_TRUE(record->GetField("a").type.IsIntTy()); EXPECT_EQ(record->GetField("a").type.GetSize(), 4U); EXPECT_EQ(record->GetField("a").offset, 0); ASSERT_TRUE(record->HasField("b")); EXPECT_TRUE(record->GetField("b").type.IsIntTy()); EXPECT_EQ(record->GetField("b").type.GetSize(), 4U); EXPECT_EQ(record->GetField("b").offset, 0); ASSERT_TRUE(record->HasField("c")); EXPECT_TRUE(record->GetField("c").type.IsIntTy()); EXPECT_EQ(record->GetField("c").type.GetSize(), 4U); EXPECT_EQ(record->GetField("c").offset, 4); } #ifdef HAVE_LIBLLDB #include "dwarf_common.h" class field_analyser_dwarf : public test_dwarf {}; TEST_F(field_analyser_dwarf, uprobe_args) { std::string uprobe = "uprobe:" + std::string(bin_); test(uprobe + ":func_1 { $x = args.a; }", 0); test(uprobe + ":func_2 { $x = args.b; }", 0); // Backwards compatibility test(uprobe + ":func_1 { $x = args->a; }", 0); // func_1 and func_2 have different args, but none of them // is used in probe code, so we're good -> PASS test(uprobe + ":func_1, " + uprobe + ":func_2 { }", 0); // func_1 and func_2 have different args, one of them // is used in probe code, we can't continue -> FAIL test(uprobe + ":func_1, " + uprobe + ":func_2 { $x = args.a; }", 1); // func_2 and func_3 have same args -> PASS test(uprobe + ":func_2, " + uprobe + ":func_3 { }", 0); test(uprobe + ":func_2, " + uprobe + ":func_3 { $x = args.a; }", 0); // Probes with wildcards (need non-mock BPFtrace) BPFtrace bpftrace; // func_* have different args, but none of them // is used in probe code, so we're good -> PASS test(bpftrace, uprobe + ":func_* { }", 0); // func_* have different args, one of them // is used in probe code, we can't continue -> FAIL test(bpftrace, uprobe + ":func_* { $x = args.a; }", 1); } static void CheckFieldsOrderedByOffset(const Fields &fields) { // Check order of the fields for consistent output when printing record types. EXPECT_TRUE(std::is_sorted( fields.begin(), fields.end(), [](const Field &a, const Field &b) { // We accept fields that have the same offset, but different names. // Check first that the offsets are ordered, then the names. return a.offset < b.offset || (a.offset == b.offset && a.name < b.name); })); } TEST_F(field_analyser_dwarf, parse_struct) { BPFtrace bpftrace; std::string uprobe = "uprobe:" + std::string(bin_); test(bpftrace, 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); CheckFieldsOrderedByOffset(str->fields); 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); } TEST_F(field_analyser_dwarf, parse_arrays) { BPFtrace bpftrace; std::string uprobe = "uprobe:" + std::string(bin_); test(bpftrace, uprobe + ":func_arrays {\n" " @ = (struct Arrays *) args.arr;\n" "}"); ASSERT_TRUE(bpftrace.structs.Has("struct Arrays")); auto arrs = bpftrace.structs.Lookup("struct Arrays").lock(); EXPECT_EQ(arrs->size, 64); ASSERT_EQ(arrs->fields.size(), 6U); CheckFieldsOrderedByOffset(arrs->fields); ASSERT_TRUE(arrs->HasField("int_arr")); ASSERT_TRUE(arrs->HasField("char_arr")); ASSERT_TRUE(arrs->HasField("ptr_arr")); ASSERT_TRUE(arrs->HasField("multi_dim")); ASSERT_TRUE(arrs->HasField("zero")); ASSERT_TRUE(arrs->HasField("flexible")); EXPECT_TRUE(arrs->GetField("int_arr").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("int_arr").type.GetNumElements(), 4); EXPECT_TRUE(arrs->GetField("int_arr").type.GetElementTy()->IsIntTy()); EXPECT_EQ(arrs->GetField("int_arr").type.GetSize(), 16U); EXPECT_EQ(arrs->GetField("int_arr").offset, 0); EXPECT_TRUE(arrs->GetField("char_arr").type.IsStringTy()); EXPECT_EQ(arrs->GetField("char_arr").type.GetSize(), 8U); EXPECT_EQ(arrs->GetField("char_arr").offset, 16); EXPECT_TRUE(arrs->GetField("ptr_arr").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("ptr_arr").type.GetNumElements(), 2); EXPECT_TRUE(arrs->GetField("ptr_arr").type.GetElementTy()->IsPtrTy()); EXPECT_EQ(arrs->GetField("ptr_arr").type.GetSize(), 2 * sizeof(uintptr_t)); EXPECT_EQ(arrs->GetField("ptr_arr").offset, 24); ASSERT_TRUE(arrs->HasField("multi_dim")); EXPECT_TRUE(arrs->GetField("multi_dim").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("multi_dim").offset, 40); EXPECT_EQ(arrs->GetField("multi_dim").type.GetSize(), 24U); EXPECT_EQ(arrs->GetField("multi_dim").type.GetNumElements(), 3); EXPECT_TRUE(arrs->GetField("multi_dim").type.GetElementTy()->IsArrayTy()); EXPECT_EQ(arrs->GetField("multi_dim").type.GetElementTy()->GetSize(), 8U); EXPECT_EQ(arrs->GetField("multi_dim").type.GetElementTy()->GetNumElements(), 2); EXPECT_TRUE(arrs->GetField("multi_dim") .type.GetElementTy() ->GetElementTy() ->IsIntTy()); EXPECT_EQ(arrs->GetField("multi_dim") .type.GetElementTy() ->GetElementTy() ->GetSize(), 4U); EXPECT_TRUE(arrs->GetField("zero").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("zero").type.GetNumElements(), 0); EXPECT_TRUE(arrs->GetField("zero").type.GetElementTy()->IsIntTy()); EXPECT_EQ(arrs->GetField("zero").type.GetSize(), 0U); EXPECT_EQ(arrs->GetField("zero").offset, 64); EXPECT_TRUE(arrs->GetField("flexible").type.IsArrayTy()); EXPECT_EQ(arrs->GetField("flexible").type.GetNumElements(), 0); EXPECT_TRUE(arrs->GetField("flexible").type.GetElementTy()->IsIntTy()); EXPECT_EQ(arrs->GetField("flexible").type.GetSize(), 0U); EXPECT_EQ(arrs->GetField("flexible").offset, 64); } TEST_F(field_analyser_dwarf, arrays_compound_data) { BPFtrace bpftrace; test(bpftrace, "uprobe:" + std::string{ bin_ } + ":func_array_with_compound_data {\n" " args.arr->data[0]->foo1->a;\n" "}", 0); test_arrays_compound_data(bpftrace); } static void CheckParentFields(const std::shared_ptr &cls, bool is_d_shadowed = false) { EXPECT_TRUE(cls->HasField("a")); EXPECT_TRUE(cls->GetField("a").type.IsIntTy()); EXPECT_EQ(cls->GetField("a").type.GetSize(), 4); EXPECT_EQ(cls->GetField("a").offset, 0); EXPECT_TRUE(cls->HasField("b")); EXPECT_TRUE(cls->GetField("b").type.IsIntTy()); EXPECT_EQ(cls->GetField("b").type.GetSize(), 4); EXPECT_EQ(cls->GetField("b").offset, 4); EXPECT_TRUE(cls->HasField("c")); EXPECT_TRUE(cls->GetField("c").type.IsIntTy()); EXPECT_EQ(cls->GetField("c").type.GetSize(), 4); EXPECT_EQ(cls->GetField("c").offset, 8); // The Child class also has a field 'd', which shadows the Parent's. if (!is_d_shadowed) { EXPECT_TRUE(cls->HasField("d")); EXPECT_TRUE(cls->GetField("d").type.IsIntTy()); EXPECT_EQ(cls->GetField("d").type.GetSize(), 4); EXPECT_EQ(cls->GetField("d").offset, 12); } } static void CheckChildFields(const std::shared_ptr &cls) { CheckParentFields(cls, true); // Child::d masks Parent::d EXPECT_TRUE(cls->HasField("d")); EXPECT_TRUE(cls->GetField("d").type.IsIntTy()); EXPECT_EQ(cls->GetField("d").type.GetSize(), 4); EXPECT_EQ(cls->GetField("d").offset, 16); EXPECT_TRUE(cls->HasField("e")); EXPECT_TRUE(cls->GetField("e").type.IsIntTy()); EXPECT_EQ(cls->GetField("e").type.GetSize(), 4); EXPECT_EQ(cls->GetField("e").offset, 20); EXPECT_TRUE(cls->HasField("f")); EXPECT_TRUE(cls->GetField("f").type.IsIntTy()); EXPECT_EQ(cls->GetField("f").type.GetSize(), 4); EXPECT_EQ(cls->GetField("f").offset, 24); } static void CheckGrandChildFields(const std::shared_ptr &cls) { CheckChildFields(cls); EXPECT_TRUE(cls->HasField("g")); EXPECT_TRUE(cls->GetField("g").type.IsIntTy()); EXPECT_EQ(cls->GetField("g").type.GetSize(), 4); EXPECT_EQ(cls->GetField("g").offset, 28); } TEST_F(field_analyser_dwarf, parse_class) { BPFtrace bpftrace; std::string uprobe = "uprobe:" + std::string(cxx_bin_); test(bpftrace, uprobe + ":cpp:func_1 { $x = args.p->a; }", 0); ASSERT_TRUE(bpftrace.structs.Has("Parent")); auto cls = bpftrace.structs.Lookup("Parent").lock(); ASSERT_TRUE(cls->HasFields()); EXPECT_EQ(cls->fields.size(), 4); EXPECT_EQ(cls->size, 16); CheckFieldsOrderedByOffset(cls->fields); CheckParentFields(cls); } TEST_F(field_analyser_dwarf, parse_inheritance) { BPFtrace bpftrace; std::string uprobe = "uprobe:" + std::string(cxx_bin_); test(bpftrace, uprobe + ":cpp:func_1 { $x = args.c->a; }", 0); ASSERT_TRUE(bpftrace.structs.Has("Child")); auto cls = bpftrace.structs.Lookup("Child").lock(); ASSERT_TRUE(cls->HasFields()); // The Child class has a total of 7 fields: // Parent: a, b, c, d // Child: d, e, f // Child::d masks Parent::d, so only 6 fields are listed. ASSERT_EQ(cls->fields.size(), (4 - 1) + 3); ASSERT_EQ(cls->size, 16 + 12); CheckFieldsOrderedByOffset(cls->fields); CheckChildFields(cls); } TEST_F(field_analyser_dwarf, parse_inheritance_chain) { BPFtrace bpftrace; std::string uprobe = "uprobe:" + std::string(cxx_bin_); test(bpftrace, uprobe + ":cpp:func_2 { $x = args.lc->a; }", 0); ASSERT_TRUE(bpftrace.structs.Has("GrandChild")); auto cls = bpftrace.structs.Lookup("GrandChild").lock(); ASSERT_TRUE(cls->HasFields()); // The GrandChild class has a total of 8 fields: // Parent: a, b, c, d // Child: d, e, f // GrandChild: g // Child::d masks Parent::d, so only 7 fields are listed. ASSERT_EQ(cls->fields.size(), (4 - 1) + 3 + 1); ASSERT_EQ(cls->size, 16 + 12 + 4); CheckFieldsOrderedByOffset(cls->fields); CheckGrandChildFields(cls); } TEST_F(field_analyser_dwarf, parse_inheritance_multi) { BPFtrace bpftrace; std::string uprobe = "uprobe:" + std::string(cxx_bin_); test(bpftrace, uprobe + ":cpp:func_3 { $x = args.m->abc; }", 0); std::shared_ptr cls; ASSERT_TRUE(bpftrace.structs.Has("struct Multi")); cls = bpftrace.structs.Lookup("struct Multi").lock(); ASSERT_TRUE(cls->HasFields()); ASSERT_EQ(cls->fields.size(), 7); ASSERT_EQ(cls->size, 32); CheckFieldsOrderedByOffset(cls->fields); CheckParentFields(cls); EXPECT_TRUE(cls->HasField("x")); EXPECT_TRUE(cls->GetField("x").type.IsIntTy()); EXPECT_EQ(cls->GetField("x").type.GetSize(), 4); EXPECT_EQ(cls->GetField("x").offset, 16); EXPECT_TRUE(cls->HasField("abc")); EXPECT_TRUE(cls->GetField("abc").type.IsIntTy()); EXPECT_EQ(cls->GetField("abc").type.GetSize(), 4); EXPECT_EQ(cls->GetField("abc").offset, 20); EXPECT_TRUE(cls->HasField("rabc")); EXPECT_TRUE(cls->GetField("rabc").type.IsRefTy()); EXPECT_EQ(cls->GetField("rabc").type.GetSize(), 8); EXPECT_EQ(cls->GetField("rabc").offset, 24); } TEST_F(field_analyser_dwarf, parse_struct_anonymous_fields) { GTEST_SKIP() << "Anonymous fields not supported #3084"; BPFtrace bpftrace; std::string uprobe = "uprobe:" + std::string(bin_); test(bpftrace, uprobe + ":func_1 { $x = args.foo2->g; }", 0); ASSERT_TRUE(bpftrace.structs.Has("struct Foo2")); auto str = bpftrace.structs.Lookup("struct Foo2").lock(); ASSERT_TRUE(str->HasFields()); ASSERT_EQ(str->fields.size(), 3); ASSERT_EQ(str->size, 72); CheckFieldsOrderedByOffset(str->fields); 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("f")); ASSERT_TRUE(str->GetField("f").type.IsRecordTy()); ASSERT_EQ(str->GetField("f").type.GetSize(), 64); ASSERT_EQ(str->GetField("f").offset, 8); ASSERT_TRUE(str->HasField("g")); ASSERT_TRUE(str->GetField("g").type.IsIntTy()); ASSERT_EQ(str->GetField("g").type.GetSize(), 1); ASSERT_EQ(str->GetField("g").offset, 8); } TEST_F(field_analyser_dwarf, dwarf_types_bitfields) { BPFtrace bpftrace; std::string uprobe = "uprobe:" + std::string(bin_); test(bpftrace, uprobe + ":func_1 { @ = ((struct task_struct *)curtask)->pid; }", 0); ASSERT_TRUE(bpftrace.structs.Has("struct task_struct")); auto task_struct = bpftrace.structs.Lookup("struct task_struct").lock(); CheckFieldsOrderedByOffset(task_struct->fields); // clang-tidy doesn't seem to acknowledge that ASSERT_*() will // return from function so that these are in fact checked accesses. // // NOLINTBEGIN(bugprone-unchecked-optional-access) ASSERT_TRUE(task_struct->HasField("a")); EXPECT_TRUE(task_struct->GetField("a").type.IsIntTy()); EXPECT_EQ(task_struct->GetField("a").type.GetSize(), 4U); ASSERT_TRUE(task_struct->GetField("a").bitfield.has_value()); EXPECT_TRUE(task_struct->GetField("a").offset == 8 || task_struct->GetField("a").offset == 9); if (task_struct->GetField("a").offset == 8) { // DWARF < 4 EXPECT_EQ(task_struct->GetField("a").bitfield->read_bytes, 0x3U); EXPECT_EQ(task_struct->GetField("a").bitfield->access_rshift, 12U); EXPECT_EQ(task_struct->GetField("a").bitfield->mask, 0xFFU); } else { // DWARF >= 4 EXPECT_EQ(task_struct->GetField("a").bitfield->read_bytes, 0x2U); EXPECT_EQ(task_struct->GetField("a").bitfield->access_rshift, 4U); EXPECT_EQ(task_struct->GetField("a").bitfield->mask, 0xFFU); } ASSERT_TRUE(task_struct->HasField("b")); EXPECT_TRUE(task_struct->GetField("b").type.IsIntTy()); EXPECT_EQ(task_struct->GetField("b").type.GetSize(), 4U); ASSERT_TRUE(task_struct->GetField("b").bitfield.has_value()); EXPECT_TRUE(task_struct->GetField("b").offset == 8 || task_struct->GetField("b").offset == 10); if (task_struct->GetField("b").offset == 8) { // DWARF < 4 EXPECT_EQ(task_struct->GetField("b").bitfield->read_bytes, 0x3U); EXPECT_EQ(task_struct->GetField("b").bitfield->access_rshift, 20U); EXPECT_EQ(task_struct->GetField("b").bitfield->mask, 0x1U); } else { // DWARF >= 4 EXPECT_EQ(task_struct->GetField("b").bitfield->read_bytes, 0x1U); EXPECT_EQ(task_struct->GetField("b").bitfield->access_rshift, 4U); EXPECT_EQ(task_struct->GetField("b").bitfield->mask, 0x1U); } ASSERT_TRUE(task_struct->HasField("c")); EXPECT_TRUE(task_struct->GetField("c").type.IsIntTy()); EXPECT_EQ(task_struct->GetField("c").type.GetSize(), 4U); ASSERT_TRUE(task_struct->GetField("c").bitfield.has_value()); EXPECT_TRUE(task_struct->GetField("c").offset == 8 || task_struct->GetField("c").offset == 10); if (task_struct->GetField("c").offset == 8) { // DWARF < 4 EXPECT_EQ(task_struct->GetField("c").bitfield->read_bytes, 0x3U); EXPECT_EQ(task_struct->GetField("c").bitfield->access_rshift, 21U); EXPECT_EQ(task_struct->GetField("c").bitfield->mask, 0x7U); } else { // DWARF >= 4 EXPECT_EQ(task_struct->GetField("c").bitfield->read_bytes, 0x1U); EXPECT_EQ(task_struct->GetField("c").bitfield->access_rshift, 5U); EXPECT_EQ(task_struct->GetField("c").bitfield->mask, 0x7U); } ASSERT_TRUE(task_struct->HasField("d")); EXPECT_TRUE(task_struct->GetField("d").type.IsIntTy()); EXPECT_EQ(task_struct->GetField("d").type.GetSize(), 4U); EXPECT_EQ(task_struct->GetField("d").offset, 12); ASSERT_TRUE(task_struct->GetField("d").bitfield.has_value()); EXPECT_EQ(task_struct->GetField("d").bitfield->read_bytes, 0x3U); EXPECT_EQ(task_struct->GetField("d").bitfield->access_rshift, 0U); EXPECT_EQ(task_struct->GetField("d").bitfield->mask, 0xFFFFFU); // NOLINTEND(bugprone-unchecked-optional-access) } TEST(field_analyser_subprog, struct_cast) { test("struct x { int a; } fn f(): void { $s = (struct x *)0; }", 0); } #endif // HAVE_LIBLLDB } // namespace bpftrace::test::field_analyser bpftrace-0.23.2/tests/function_registry.cpp000066400000000000000000000310121477746507000210250ustar00rootroot00000000000000#include "functions.h" #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "struct.h" namespace bpftrace::test::function_registry { using ::testing::Property; using ::testing::StrEq; using ::testing::Throws; class TestFunctionRegistryPopulated : public ::testing::Test { protected: TestFunctionRegistryPopulated() { unique_no_args_ = reg_.add( Function::Origin::Builtin, "unique_no_args", CreateNone(), {}); unique_int8_ = reg_.add(Function::Origin::Builtin, "unique_int8", CreateNone(), { Param{ "a", CreateInt8() }, }); unique_int16_ = reg_.add(Function::Origin::Builtin, "unique_int16", CreateNone(), { Param{ "a", CreateInt16() }, }); unique_int32_ = reg_.add(Function::Origin::Builtin, "unique_int32", CreateNone(), { Param{ "a", CreateInt32() }, }); unique_int64_ = reg_.add(Function::Origin::Builtin, "unique_int64", CreateNone(), { Param{ "a", CreateInt64() }, }); unique_uint8_ = reg_.add(Function::Origin::Builtin, "unique_uint8", CreateNone(), { Param{ "a", CreateUInt8() }, }); unique_uint16_ = reg_.add(Function::Origin::Builtin, "unique_uint16", CreateNone(), { Param{ "a", CreateUInt16() }, }); unique_uint32_ = reg_.add(Function::Origin::Builtin, "unique_uint32", CreateNone(), { Param{ "a", CreateUInt32() }, }); unique_uint64_ = reg_.add(Function::Origin::Builtin, "unique_uint64", CreateNone(), { Param{ "a", CreateUInt64() }, }); unique_string_ = reg_.add(Function::Origin::Builtin, "unique_string", CreateNone(), { Param{ "a", CreateString(32) }, }); unique_tuple_ = reg_.add( Function::Origin::Builtin, "unique_tuple", CreateNone(), { Param{ "a", CreateTuple(structs_.AddTuple( { CreateInt32(), CreateString(64) })) }, }); auto unique_struct_arg = structs_.Add("unique_struct_arg", 8).lock(); unique_struct_arg->AddField("x", CreateInt64(), 0); unique_struct_ = reg_.add( Function::Origin::Builtin, "unique_struct", CreateNone(), { Param{ "a", CreateRecord("unique_struct_arg", unique_struct_arg) }, }); takes_c_string_ = reg_.add(Function::Origin::Builtin, "takes_c_string", CreateNone(), { Param{ "a", CreatePointer(CreateInt8()) }, }); overloaded_origin_b_s_builtin_ = reg_.add( Function::Origin::Builtin, "overloaded_origin_b_s", CreateNone(), {}); overloaded_origin_b_s_script_ = reg_.add( Function::Origin::Script, "overloaded_origin_b_s", CreateNone(), {}); overloaded_origin_b_e_builtin_ = reg_.add( Function::Origin::Builtin, "overloaded_origin_b_e", CreateNone(), {}); overloaded_origin_b_e_external_ = reg_.add( Function::Origin::External, "overloaded_origin_b_e", CreateNone(), {}); } FunctionRegistry reg_; StructManager structs_; const Function *unique_no_args_ = nullptr; const Function *unique_int8_ = nullptr; const Function *unique_int16_ = nullptr; const Function *unique_int32_ = nullptr; const Function *unique_int64_ = nullptr; const Function *unique_uint8_ = nullptr; const Function *unique_uint16_ = nullptr; const Function *unique_uint32_ = nullptr; const Function *unique_uint64_ = nullptr; const Function *unique_string_ = nullptr; const Function *unique_tuple_ = nullptr; const Function *unique_struct_ = nullptr; const Function *takes_c_string_ = nullptr; const Function *overloaded_origin_b_s_builtin_ = nullptr; const Function *overloaded_origin_b_s_script_ = nullptr; const Function *overloaded_origin_b_e_builtin_ = nullptr; const Function *overloaded_origin_b_e_external_ = nullptr; void test(const std::string &func_name, const std::vector &arg_types, const Function *expected_result); void test(const std::string &func_name, const std::vector &arg_types, std::string_view expected_error); }; void TestFunctionRegistryPopulated::test( const std::string &func_name, const std::vector &arg_types, const Function *expected_result) { std::stringstream out; EXPECT_EQ(expected_result, reg_.get("", func_name, arg_types, out)); EXPECT_EQ("", out.str()); } void TestFunctionRegistryPopulated::test( const std::string &func_name, const std::vector &arg_types, std::string_view expected_error) { std::stringstream out; EXPECT_EQ(nullptr, reg_.get("", func_name, arg_types, out)); if (expected_error[0] == '\n') expected_error.remove_prefix(1); EXPECT_EQ(expected_error, out.str()); } TEST_F(TestFunctionRegistryPopulated, does_not_exist) { test("does_not_exist", {}, R"( ERROR: Function not found: 'does_not_exist' )"); } TEST_F(TestFunctionRegistryPopulated, unique_wrong_args) { test("unique_int32", { CreateString(32) }, R"( ERROR: Cannot call function 'unique_int32' using argument types: (string[32]) HINT: Candidate function: unique_int32(int32) )"); test("unique_int32", { CreateInt32(), CreateInt32() }, R"( ERROR: Cannot call function 'unique_int32' using argument types: (int32, int32) HINT: Candidate function: unique_int32(int32) )"); } TEST_F(TestFunctionRegistryPopulated, unique_exact_args) { test("unique_no_args", {}, unique_no_args_); } TEST_F(TestFunctionRegistryPopulated, unique_integers) { // This test goes through every combination of [u]intX -> [u]intY // Include one hardcoded test so we can see clearly what the full error // messages look like. This acts as a sanity-check for the generated error // messages below. test("unique_int8", { CreateInt16() }, R"( ERROR: Cannot call function 'unique_int8' using argument types: (int16) HINT: Candidate function: unique_int8(int8) )"); // columns represent the type of the parameter being passed in, in order: // int8, int16, int32, int64, uint8, uint16, uint32, uint64 // clang-format off const std::vector> expected = { { 1, 0, 0, 0, 0, 0, 0, 0 }, // to int8 { 1, 1, 0, 0, 1, 0, 0, 0 }, // to int16 { 1, 1, 1, 0, 1, 1, 0, 0 }, // to int32 { 1, 1, 1, 1, 1, 1, 1, 0 }, // to int64 { 0, 0, 0, 0, 1, 0, 0, 0 }, // to uint8 { 0, 0, 0, 0, 1, 1, 0, 0 }, // to uint16 { 0, 0, 0, 0, 1, 1, 1, 0 }, // to uint32 { 0, 0, 0, 0, 1, 1, 1, 1 }, // to uint64 }; // clang-format on const std::vector> to = { { "int8", unique_int8_ }, { "int16", unique_int16_ }, { "int32", unique_int32_ }, { "int64", unique_int64_ }, { "uint8", unique_uint8_ }, { "uint16", unique_uint16_ }, { "uint32", unique_uint32_ }, { "uint64", unique_uint64_ }, }; const std::vector> from = { { "int8", CreateInt8() }, { "int16", CreateInt16() }, { "int32", CreateInt32() }, { "int64", CreateInt64() }, { "uint8", CreateUInt8() }, { "uint16", CreateUInt16() }, { "uint32", CreateUInt32() }, { "uint64", CreateUInt64() }, }; for (size_t i = 0; i < to.size(); i++) { for (size_t j = 0; j < from.size(); j++) { std::string func_name = "unique_" + to[i].first; if (expected[i][j]) { // valid test(func_name, { from[j].second }, to[i].second); } else { // invalid std::string expected_error = R"( ERROR: Cannot call function ')" + func_name + "' using argument types: (" + from[j].first + R"() HINT: Candidate function: )" + func_name + "(" + to[i].first + R"() )"; test(func_name, { from[j].second }, expected_error); } } } } TEST_F(TestFunctionRegistryPopulated, unique_string) { test("unique_string", { CreateString(10) }, unique_string_); test("unique_string", { CreateString(32) }, unique_string_); test("unique_string", { CreateString(64) }, unique_string_); } TEST_F(TestFunctionRegistryPopulated, unique_tuple) { auto tuple1 = CreateTuple( structs_.AddTuple({ CreateInt16(), CreateString(64) })); auto tuple2 = CreateTuple( structs_.AddTuple({ CreateInt32(), CreateString(64) })); auto tuple3 = CreateTuple( structs_.AddTuple({ CreateInt64(), CreateString(64) })); test("unique_tuple", { tuple1 }, unique_tuple_); test("unique_tuple", { tuple2 }, unique_tuple_); test("unique_tuple", { tuple3 }, R"( ERROR: Cannot call function 'unique_tuple' using argument types: ((int64,string[64])) HINT: Candidate function: unique_tuple((int32,string[64])) )"); // Can't pass deconstructed tuple fields as multiple arguments test("unique_tuple", { CreateInt32(), CreateString(64) }, R"( ERROR: Cannot call function 'unique_tuple' using argument types: (int32, string[64]) HINT: Candidate function: unique_tuple((int32,string[64])) )"); } TEST_F(TestFunctionRegistryPopulated, unique_struct) { auto exact_struct = structs_.Lookup("unique_struct_arg"); auto compatible_struct = structs_.Add("same_layout_as_unique_struct_arg", 8).lock(); compatible_struct->AddField("x", CreateInt64(), 0); auto different_struct = structs_.Add("different_layout_to_unique_struct_arg", 2).lock(); different_struct->AddField("a", CreateInt8(), 0); different_struct->AddField("b", CreateInt8(), 0); test("unique_struct", { CreateRecord("unique_struct_arg", exact_struct) }, unique_struct_); test("unique_struct", { CreateRecord("same_layout_as_unique_struct_arg", compatible_struct) }, R"( ERROR: Cannot call function 'unique_struct' using argument types: (same_layout_as_unique_struct_arg) HINT: Candidate function: unique_struct(unique_struct_arg) )"); test("unique_struct", { CreateRecord("different_layout_to_unique_struct_arg", different_struct) }, R"( ERROR: Cannot call function 'unique_struct' using argument types: (different_layout_to_unique_struct_arg) HINT: Candidate function: unique_struct(unique_struct_arg) )"); } TEST_F(TestFunctionRegistryPopulated, string_to_pointer) { test("takes_c_string", { CreateString(64) }, takes_c_string_); } TEST_F(TestFunctionRegistryPopulated, overloaded_origin) { test("overloaded_origin_b_s", {}, overloaded_origin_b_s_script_); test("overloaded_origin_b_e", {}, overloaded_origin_b_e_external_); } TEST(TestFunctionRegistry, add_namespaced) { std::stringstream out; // To suppress (expected) errors from unit test output FunctionRegistry reg; auto *foo = reg.add(Function::Origin::Script, "ns", "foo", CreateNone(), {}); EXPECT_EQ(nullptr, reg.get("", "foo", {}, out)); EXPECT_EQ(foo, reg.get("ns", "foo", {}, out)); } TEST(TestFunctionRegistry, add_duplicate_of_builtin) { FunctionRegistry reg; EXPECT_NE(nullptr, reg.add(Function::Origin::Builtin, "foo", CreateNone(), {})); EXPECT_NE(nullptr, reg.add(Function::Origin::Script, "foo", CreateNone(), {})); } TEST(TestFunctionRegistry, add_duplicate) { FunctionRegistry reg; EXPECT_NE(nullptr, reg.add(Function::Origin::Script, "foo", CreateNone(), {})); EXPECT_EQ(nullptr, reg.add(Function::Origin::Script, "foo", CreateNone(), {})); } } // namespace bpftrace::test::function_registry bpftrace-0.23.2/tests/log.cpp000066400000000000000000000124361477746507000160420ustar00rootroot00000000000000#include "log.h" #include "mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include namespace bpftrace::test::log { TEST(LogStream, basic) { std::ostringstream ss; const std::string content_1 = "hello world"; const std::string content_2 = "some messages 100###**"; LOG(WARNING, ss) << content_1 << content_2; EXPECT_EQ(ss.str(), "WARNING: " + content_1 + content_2 + "\n"); ss.str({}); LOG(ERROR, ss) << content_1 << content_2 << content_1 << content_2 << "\n" << content_1 << content_2 << content_1 << content_2 << "\n"; const std::string content_long = content_1 + content_2 + content_1 + content_2; EXPECT_EQ(ss.str(), "ERROR: " + content_long + "\n" + content_long + "\n\n"); ss.str({}); // test macro with 1 argument ENABLE_LOG(V1); auto cerr_buf = std::cerr.rdbuf(ss.rdbuf()); LOG(V1) << content_1 << content_2; EXPECT_EQ(ss.str(), content_1 + content_2 + "\n"); std::cerr.rdbuf(cerr_buf); DISABLE_LOG(V1); } TEST(LogStream, basic_colorized) { std::ostringstream ss; const std::string content_1 = "hello world"; const std::string content_2 = "some messages 100###**"; const std::string warning_color = std::string(bpftrace::LogColor::YELLOW); const std::string error_color = std::string(bpftrace::LogColor::RED); const std::string default_color = std::string(bpftrace::LogColor::RESET); Log::get().set_colorize(true); LOG(WARNING, ss) << content_1 << content_2; EXPECT_EQ(ss.str(), warning_color + "WARNING: " + content_1 + content_2 + default_color + "\n"); ss.str({}); LOG(ERROR, ss) << content_1 << content_2 << content_1 << content_2 << "\n" << content_1 << content_2 << content_1 << content_2 << "\n"; const std::string content_long = content_1 + content_2 + content_1 + content_2; EXPECT_EQ(ss.str(), error_color + "ERROR: " + content_long + "\n" + content_long + "\n" + default_color + "\n"); Log::get().set_colorize(false); ss.str({}); // test macro with 1 argument ENABLE_LOG(V1); auto cerr_buf = std::cerr.rdbuf(ss.rdbuf()); LOG(V1) << content_1 << content_2; EXPECT_EQ(ss.str(), content_1 + content_2 + "\n"); std::cerr.rdbuf(cerr_buf); DISABLE_LOG(V1); } TEST(LogStream, with_location) { std::ostringstream ss; const std::string filename = "stdin"; const std::string source = "i:s:1 { print(1, 2); }"; const std::string expected = "stdin:1:9-20: ERROR: Non-map print() only takes 1 argument, 2 " "found\ni:s:1 { print(1, 2); }\n ~~~~~~~~~~~\n"; bpftrace::location loc(bpftrace::position(nullptr, 1, 9), bpftrace::position(nullptr, 1, 20)); Log::get().set_source(filename, source); LOG(ERROR, loc, ss) << "Non-map print() only takes 1 argument, 2 found"; EXPECT_EQ(ss.str(), expected); } TEST(LogStream, with_location_colorized) { std::ostringstream ss; const std::string filename = "stdin"; const std::string source = "i:s:1 { print(1, 2); }"; const std::string error_msg = "stdin:1:9-20: ERROR: Non-map print() only takes 1 argument, 2 found\n"; const std::string source_marker = "i:s:1 { print(1, 2); }\n ~~~~~~~~~~~"; const std::string error_color = std::string(bpftrace::LogColor::RED); const std::string default_color = std::string(bpftrace::LogColor::RESET); const std::string expected = error_color + error_msg + default_color + source_marker + "\n"; bpftrace::location loc(bpftrace::position(nullptr, 1, 9), bpftrace::position(nullptr, 1, 20)); Log::get().set_source(filename, source); Log::get().set_colorize(true); LOG(ERROR, loc, ss) << "Non-map print() only takes 1 argument, 2 found"; Log::get().set_colorize(false); EXPECT_EQ(ss.str(), expected); } TEST(LogStream, with_location_colorized_multi_lines) { std::ostringstream ss; const std::string filename = "stdin"; const std::string source = "i:s:1 { print(1,\n 2, \n 3); }"; const std::string error_msg = "stdin:1-3: ERROR: Non-map print() only takes 1 argument, 3 found"; const std::string error_color = std::string(bpftrace::LogColor::RED); const std::string default_color = std::string(bpftrace::LogColor::RESET); const std::string expected = error_color + error_msg + "\n" + default_color; bpftrace::location loc(bpftrace::position(nullptr, 1), bpftrace::position(nullptr, 3)); Log::get().set_source(filename, source); Log::get().set_colorize(true); LOG(ERROR, loc, ss) << "Non-map print() only takes 1 argument, 3 found"; Log::get().set_colorize(false); EXPECT_EQ(ss.str(), expected); } TEST(Log, disable_log_type) { std::ostringstream ss; const std::string content = "This is the warning message"; LOG(WARNING, ss) << content; EXPECT_EQ(ss.str(), "WARNING: " + content + "\n"); ss.str({}); Log::get().disable(LogType::WARNING); LOG(WARNING, ss) << content; EXPECT_EQ(ss.str(), ""); // make sure other log types are not affected LOG(ERROR, ss) << content; EXPECT_EQ(ss.str(), "ERROR: " + content + "\n"); ss.str({}); Log::get().enable(LogType::WARNING); LOG(WARNING, ss) << content; EXPECT_EQ(ss.str(), "WARNING: " + content + "\n"); ss.str({}); } } // namespace bpftrace::test::log bpftrace-0.23.2/tests/main.cpp000066400000000000000000000007351477746507000162040ustar00rootroot00000000000000#include "gtest/gtest.h" class ThrowListener : public testing::EmptyTestEventListener { void OnTestPartResult(const testing::TestPartResult &result) override { if (result.type() == testing::TestPartResult::kFatalFailure) { throw testing::AssertionException(result); } } }; int main(int argc, char *argv[]) { ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); return RUN_ALL_TESTS(); } bpftrace-0.23.2/tests/mocks.cpp000066400000000000000000000162321477746507000163730ustar00rootroot00000000000000#include "mocks.h" #include "tracefs.h" namespace bpftrace::test { using ::testing::_; using ::testing::NiceMock; using ::testing::Return; using ::testing::StrictMock; void setup_mock_probe_matcher(MockProbeMatcher &matcher) { ON_CALL(matcher, get_symbols_from_traceable_funcs(false)).WillByDefault([]() { std::string ksyms = "SyS_read\n" "sys_read\n" "sys_write\n" "my_one\n" "my_two\n"; auto myval = std::unique_ptr(new std::istringstream(ksyms)); return myval; }); ON_CALL(matcher, get_symbols_from_traceable_funcs(true)).WillByDefault([]() { std::string ksyms = "kernel_mod:func_in_mod\n" "kernel_mod:other_func_in_mod\n" "other_kernel_mod:func_in_mod\n"; auto myval = std::unique_ptr(new std::istringstream(ksyms)); return myval; }); ON_CALL(matcher, get_symbols_from_file(tracefs::available_events())) .WillByDefault([](const std::string &) { std::string tracepoints = "sched:sched_one\n" "sched:sched_two\n" "sched:foo\n" "sched_extra:sched_extra\n" "notsched:bar\n" "file:filename\n" "tcp:some_tcp_tp\n" "btf:tag\n"; return std::unique_ptr( new std::istringstream(tracepoints)); }); std::string sh_usyms = "/bin/sh:first_open\n" "/bin/sh:second_open\n" "/bin/sh:open_as_well\n" "/bin/sh:something_else\n" "/bin/sh:cpp_mangled\n" "/bin/sh:_Z11cpp_mangledi\n" "/bin/sh:_Z11cpp_mangledv\n" "/bin/sh:_Z18cpp_mangled_suffixv\n"; std::string bash_usyms = "/bin/bash:first_open\n"; ON_CALL(matcher, get_func_symbols_from_file(_, "/bin/sh")) .WillByDefault([sh_usyms](int, const std::string &) { return std::unique_ptr(new std::istringstream(sh_usyms)); }); ON_CALL(matcher, get_func_symbols_from_file(_, "/bin/*sh")) .WillByDefault([sh_usyms, bash_usyms](int, const std::string &) { return std::unique_ptr( new std::istringstream(sh_usyms + bash_usyms)); }); std::string sh_usdts = "/bin/sh:prov1:tp1\n" "/bin/sh:prov1:tp2\n" "/bin/sh:prov2:tp\n" "/bin/sh:prov2:notatp\n" "/bin/sh:nahprov:tp\n"; std::string bash_usdts = "/bin/bash:prov1:tp3\n"; ON_CALL(matcher, get_symbols_from_usdt(_, "/bin/sh")) .WillByDefault([sh_usdts](int, const std::string &) { return std::unique_ptr(new std::istringstream(sh_usdts)); }); ON_CALL(matcher, get_symbols_from_usdt(_, "/bin/*sh")) .WillByDefault([sh_usdts, bash_usdts](int, const std::string &) { return std::unique_ptr( new std::istringstream(sh_usdts + bash_usdts)); }); } void setup_mock_bpftrace(MockBPFtrace &bpftrace) { bpftrace.delta_taitime_ = timespec{}; bpftrace.parse_btf({ "vmlinux" }); // Fill in some default tracepoint struct definitions bpftrace.structs.Add("struct _tracepoint_sched_sched_one", 8); bpftrace.structs.Lookup("struct _tracepoint_sched_sched_one") .lock() ->AddField("common_field", CreateUInt64(), 8, std::nullopt, false); bpftrace.structs.Add("struct _tracepoint_sched_sched_two", 8); bpftrace.structs.Lookup("struct _tracepoint_sched_sched_two") .lock() ->AddField("common_field", CreateUInt64(), 16, // different offset than // sched_one.common_field std::nullopt, false); bpftrace.structs.Add("struct _tracepoint_sched_extra_sched_extra", 8); bpftrace.structs.Lookup("struct _tracepoint_sched_extra_sched_extra") .lock() ->AddField("common_field", CreateUInt64(), 24, // different offset than // sched_(one|two).common_field std::nullopt, false); bpftrace.structs.Add("struct _tracepoint_tcp_some_tcp_tp", 16); bpftrace.structs.Lookup("struct _tracepoint_tcp_some_tcp_tp") .lock() ->AddField( "saddr_v6", CreateArray(16, CreateUInt(8)), 8, std::nullopt, false); auto ptr_type = CreatePointer(CreateInt8()); bpftrace.structs.Add("struct _tracepoint_file_filename", 8); bpftrace.structs.Lookup("struct _tracepoint_file_filename") .lock() ->AddField("common_field", CreateUInt64(), 0, std::nullopt, false); bpftrace.structs.Lookup("struct _tracepoint_file_filename") .lock() ->AddField("filename", ptr_type, 8, std::nullopt, false); bpftrace.structs.Add("struct _tracepoint_btf_tag", 16); auto ptr_type_w_tag = CreatePointer(CreateInt8()); ptr_type_w_tag.SetBtfTypeTags({ "rcu" }); auto ptr_type_w_bad_tag = CreatePointer(CreateInt8()); ptr_type_w_bad_tag.SetBtfTypeTags({ "rcu", "percpu" }); bpftrace.structs.Lookup("struct _tracepoint_btf_tag") .lock() ->AddField("parent", ptr_type_w_tag, 8, std::nullopt, false); bpftrace.structs.Lookup("struct _tracepoint_btf_tag") .lock() ->AddField("real_parent", ptr_type_w_bad_tag, 16, std::nullopt, false); // Even though this is set to 1 by default, make it 0 here // to reduce the amount of repeated generated IR in the codegen tests bpftrace.helper_check_level_ = 0; } std::unique_ptr get_mock_bpftrace() { auto bpftrace = std::make_unique>(); setup_mock_bpftrace(*bpftrace); auto probe_matcher = std::make_unique>( bpftrace.get()); setup_mock_probe_matcher(*probe_matcher); bpftrace->set_mock_probe_matcher(std::move(probe_matcher)); bpftrace->feature_ = std::make_unique(true); return bpftrace; } std::unique_ptr get_strict_mock_bpftrace() { auto bpftrace = std::make_unique>(); setup_mock_bpftrace(*bpftrace); auto probe_matcher = std::make_unique>( bpftrace.get()); setup_mock_probe_matcher(*probe_matcher); bpftrace->set_mock_probe_matcher(std::move(probe_matcher)); bpftrace->feature_ = std::make_unique(true); return bpftrace; } std::unique_ptr get_mock_usdt_helper(int num_locations) { auto usdt_helper = std::make_unique>(); ON_CALL(*usdt_helper, find(_, _, _, _)) .WillByDefault([num_locations](int, const std::string &, const std::string &, const std::string &) { return usdt_probe_entry{ .path = "", .provider = "", .name = "", .semaphore_offset = 0, .num_locations = num_locations, }; }); return usdt_helper; } } // namespace bpftrace::test bpftrace-0.23.2/tests/mocks.h000066400000000000000000000130471477746507000160410ustar00rootroot00000000000000#pragma once #include "gmock/gmock.h" #include "bpffeature.h" #include "bpftrace.h" #include "child.h" #include "probe_matcher.h" #include "procmon.h" namespace bpftrace { namespace test { class MockProbeMatcher : public ProbeMatcher { public: MockProbeMatcher(BPFtrace *bpftrace) : ProbeMatcher(bpftrace) { } #pragma GCC diagnostic push #ifdef __clang__ #pragma GCC diagnostic ignored "-Winconsistent-missing-override" #endif MOCK_CONST_METHOD1(get_symbols_from_file, std::unique_ptr(const std::string &path)); MOCK_CONST_METHOD1(get_symbols_from_traceable_funcs, std::unique_ptr(bool with_modules)); MOCK_CONST_METHOD2(get_symbols_from_usdt, std::unique_ptr(int pid, const std::string &target)); MOCK_CONST_METHOD2(get_func_symbols_from_file, std::unique_ptr(int pid, const std::string &path)); #pragma GCC diagnostic pop }; class MockBPFtrace : public BPFtrace { public: std::vector get_probes() { return resources.probes; } std::unordered_map get_special_probes() { return resources.special_probes; } int resolve_uname(const std::string &name, struct symbol *sym, const std::string &path) const override { (void)path; sym->name = name; if (name == "cpp_mangled(int)") { return -1; } else if (name[0] >= 'A' && name[0] <= 'z') { sym->address = 12345; sym->size = 4; } else { auto fields = split_string(name, '_'); sym->address = std::stoull(fields.at(0)); sym->size = std::stoull(fields.at(1)); } return 0; } bool is_traceable_func( const std::string &__attribute__((unused))) const override { return true; } std::unordered_set get_func_modules( const std::string &__attribute__((unused))) const override { return { "mock_vmlinux" }; } const struct stat &get_pidns_self_stat() const override { static const struct stat init_pid_namespace = []() { struct stat s {}; s.st_ino = 0xeffffffc; // PROC_PID_INIT_INO return s; }(); static const struct stat child_pid_namespace = []() { struct stat s {}; s.st_ino = 0xf0000011; // Arbitrary user namespace return s; }(); if (mock_in_init_pid_ns) return init_pid_namespace; return child_pid_namespace; } void set_mock_probe_matcher(std::unique_ptr probe_matcher) { probe_matcher_ = std::move(probe_matcher); mock_probe_matcher = dynamic_cast(probe_matcher_.get()); } MockProbeMatcher *mock_probe_matcher; bool mock_in_init_pid_ns = true; }; std::unique_ptr get_mock_bpftrace(); std::unique_ptr get_strict_mock_bpftrace(); class MockBPFfeature : public BPFfeature { public: MockBPFfeature(bool has_features = true) { has_send_signal_ = std::make_optional(has_features); has_get_current_cgroup_id_ = std::make_optional(has_features); has_override_return_ = std::make_optional(has_features); has_prog_fentry_ = std::make_optional(has_features); has_probe_read_kernel_ = std::make_optional(has_features); has_features_ = has_features; has_d_path_ = std::make_optional(has_features); has_ktime_get_boot_ns_ = std::make_optional(has_features); has_kprobe_multi_ = std::make_optional(has_features); has_uprobe_multi_ = std::make_optional(has_features); has_skb_output_ = std::make_optional(has_features); map_ringbuf_ = std::make_optional(has_features); has_ktime_get_tai_ns_ = std::make_optional(has_features); has_get_func_ip_ = std::make_optional(has_features); has_jiffies64_ = std::make_optional(has_features); has_for_each_map_elem_ = std::make_optional(has_features); has_get_ns_current_pid_tgid_ = std::make_optional(has_features); has_map_lookup_percpu_elem_ = std::make_optional(has_features); }; void mock_missing_kernel_func(Kfunc kfunc) { available_kernel_funcs_.emplace(kfunc, false); } bool has_features_; }; class MockChildProc : public ChildProcBase { public: MockChildProc(std::string cmd __attribute__((unused))) { child_pid_ = 1337; }; ~MockChildProc() {}; void terminate(bool force __attribute__((unused)) = false) override {}; bool is_alive() override { return true; }; void resume(void) override {}; void run(bool pause = false) override { (void)pause; }; }; class MockProcMon : public ProcMonBase { public: MockProcMon(pid_t pid) { pid_ = pid; } ~MockProcMon() override = default; bool is_alive(void) override { if (pid_ > 0) return true; else return false; } }; class MockUSDTHelper : public USDTHelper { public: MockUSDTHelper() { } #pragma GCC diagnostic push #ifdef __clang__ #pragma GCC diagnostic ignored "-Winconsistent-missing-override" #endif MOCK_METHOD4(find, std::optional(int pid, const std::string &target, const std::string &provider, const std::string &name)); #pragma GCC diagnostic pop }; std::unique_ptr get_mock_usdt_helper(int num_locations); } // namespace test } // namespace bpftrace bpftrace-0.23.2/tests/output.cpp000066400000000000000000000056201477746507000166160ustar00rootroot00000000000000#include "output.h" #include #include #include "bpfmap.h" #include "bpftrace.h" #include "mocks.h" namespace bpftrace::test::output { TEST(TextOutput, lhist_no_suffix) { std::stringstream out; std::stringstream err; TextOutput output{ out, err }; MockBPFtrace bpftrace; bpftrace.resources.maps_info["@mymap"] = MapInfo{ CreateNone(), SizedType{ Type::lhist_t, 8 }, LinearHistogramArgs{ 610000, 670000, 10000 }, {}, {} }; BpfMap map{ libbpf::BPF_MAP_TYPE_HASH, "@mymap", 8, 8, 1000 }; std::map, std::vector> values_by_key = { { { 0 }, { 0, 1, 1, 1, 1, 1, 1, 0 }, }, }; std::vector, uint64_t>> total_counts_by_key = { { { 0 }, 6 } }; output.map_hist(bpftrace, map, 0, 0, values_by_key, total_counts_by_key); // The buckets for this test case have been specifically chosen: 640000 can // also be written as 625K, while the other bucket boundaries can not be // expressed with a suffix. We should only use the suffix representation for a // bucket if all buckets can be expressed with one. EXPECT_EQ(R"(@mymap: [610000, 620000) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [620000, 630000) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [630000, 640000) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [640000, 650000) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [650000, 660000) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [660000, 670000) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| )", out.str()); EXPECT_TRUE(err.str().empty()); } TEST(TextOutput, lhist_suffix) { std::stringstream out; std::stringstream err; TextOutput output{ out, err }; MockBPFtrace bpftrace; bpftrace.resources.maps_info["@mymap"] = MapInfo{ CreateNone(), SizedType{ Type::lhist_t, 8 }, LinearHistogramArgs{ 0, 5 * 1024, 1024 }, {}, {} }; BpfMap map{ libbpf::BPF_MAP_TYPE_HASH, "@mymap", 8, 8, 1000 }; std::map, std::vector> values_by_key = { { { 0 }, { 0, 1, 1, 1, 1, 1, 0 }, }, }; std::vector, uint64_t>> total_counts_by_key = { { { 0 }, 5 } }; output.map_hist(bpftrace, map, 0, 0, values_by_key, total_counts_by_key); EXPECT_EQ(R"(@mymap: [0, 1K) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [1K, 2K) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [2K, 3K) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [3K, 4K) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [4K, 5K) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| )", out.str()); EXPECT_TRUE(err.str().empty()); } } // namespace bpftrace::test::output bpftrace-0.23.2/tests/parser.cpp000066400000000000000000002016311477746507000165520ustar00rootroot00000000000000#include #include #include "ast/passes/printer.h" #include "driver.h" #include "gtest/gtest.h" namespace bpftrace::test::parser { using Printer = ast::Printer; void test_parse_failure(BPFtrace &bpftrace, std::string_view input, std::string_view expected_error) { std::stringstream out; Driver driver(bpftrace, out); EXPECT_EQ(driver.parse_str(input), 1); if (expected_error.data()) { if (!expected_error.empty() && expected_error[0] == '\n') expected_error.remove_prefix(1); // Remove initial '\n' EXPECT_EQ(expected_error, out.str()); } } void test_parse_failure(std::string_view input, std::string_view expected_error) { BPFtrace bpftrace; test_parse_failure(bpftrace, input, expected_error); } void test(BPFtrace &bpftrace, std::string_view input, std::string_view expected) { Driver driver(bpftrace); ASSERT_EQ(driver.parse_str(input), 0); if (expected[0] == '\n') expected.remove_prefix(1); // Remove initial '\n' std::ostringstream out; Printer printer(driver.ctx, out); printer.print(); EXPECT_EQ(expected, out.str()); } void test(std::string_view input, std::string_view expected) { BPFtrace bpftrace; test(bpftrace, input, expected); } TEST(Parser, builtin_variables) { test("kprobe:f { pid }", R"( Program kprobe:f builtin: pid )"); test("kprobe:f { tid }", R"( Program kprobe:f builtin: tid )"); test("kprobe:f { cgroup }", R"( Program kprobe:f builtin: cgroup )"); test("kprobe:f { uid }", R"( Program kprobe:f builtin: uid )"); test("kprobe:f { username }", R"( Program kprobe:f builtin: username )"); test("kprobe:f { gid }", R"( Program kprobe:f builtin: gid )"); test("kprobe:f { nsecs }", R"( Program kprobe:f builtin: nsecs )"); test("kprobe:f { elapsed }", R"( Program kprobe:f builtin: elapsed )"); test("kprobe:f { numaid }", R"( Program kprobe:f builtin: numaid )"); test("kprobe:f { cpu }", R"( Program kprobe:f builtin: cpu )"); test("kprobe:f { curtask }", R"( Program kprobe:f builtin: curtask )"); test("kprobe:f { rand }", R"( Program kprobe:f builtin: rand )"); test("kprobe:f { ctx }", R"( Program kprobe:f builtin: ctx )"); test("kprobe:f { comm }", R"( Program kprobe:f builtin: comm )"); test("kprobe:f { kstack }", R"( Program kprobe:f builtin: kstack )"); test("kprobe:f { ustack }", R"( Program kprobe:f builtin: ustack )"); test("kprobe:f { arg0 }", R"( Program kprobe:f builtin: arg0 )"); test("kprobe:f { sarg0 }", R"( Program kprobe:f builtin: sarg0 )"); test("kprobe:f { retval }", R"( Program kprobe:f builtin: retval )"); test("kprobe:f { func }", R"( Program kprobe:f builtin: func )"); test("kprobe:f { probe }", R"( Program kprobe:f builtin: probe )"); test("kprobe:f { args }", R"( Program kprobe:f builtin: args )"); } TEST(Parser, positional_param) { test("kprobe:f { $1 }", R"( Program kprobe:f param: $1 )"); test_parse_failure("kprobe:f { $0 }", R"( stdin:1:12-14: ERROR: param $0 is out of integer range [1, 9223372036854775807] kprobe:f { $0 } ~~ )"); } TEST(Parser, positional_param_count) { test("kprobe:f { $# }", R"( Program kprobe:f param: $# )"); } TEST(Parser, positional_param_attachpoint) { BPFtrace bpftrace; bpftrace.add_param("foo"); bpftrace.add_param("bar"); bpftrace.add_param("baz"); test(bpftrace, "kprobe:$1 { 1 }", R"PROG(Program kprobe:foo int: 1 )PROG"); test(bpftrace, R"PROG(kprobe:$1"here" { 1 })PROG", R"PROG(Program kprobe:foohere int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:zzzzzzz:$2 { 1 })PROG", R"PROG(Program uprobe:zzzzzzz:bar int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:$1:$2 { 1 })PROG", R"PROG(Program uprobe:foo:bar int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:$2:$1 { 1 })PROG", R"PROG(Program uprobe:bar:foo int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:"zz"$2"zz":"aa"$1 { 1 })PROG", R"PROG(Program uprobe:zzbarzz:aafoo int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:$2:"aa"$1"aa" { 1 })PROG", R"PROG(Program uprobe:bar:aafooaa int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:"$1":$2 { 1 })PROG", R"PROG(Program uprobe:$1:bar int: 1 )PROG"); test(bpftrace, R"PROG(uprobe:aa$1:$2 { 1 })PROG", R"PROG(Program uprobe:aafoo:bar int: 1 )PROG"); // Error location is incorrect: #3063 test_parse_failure(bpftrace, R"(uprobe:$1a { 1 })", R"( stdin:1:1-12: ERROR: Found trailing text 'a' in positional parameter index. Try quoting the trailing text. uprobe:$1a { 1 } ~~~~~~~~~~~ stdin:1:1-18: ERROR: No attach points for probe uprobe:$1a { 1 } ~~~~~~~~~~~~~~~~ )"); test_parse_failure(bpftrace, R"(uprobe:$a { 1 })", R"( stdin:1:1-11: ERROR: syntax error, unexpected variable, expecting { uprobe:$a { 1 } ~~~~~~~~~~ )"); test_parse_failure(bpftrace, R"(uprobe:$-1 { 1 })", R"( stdin:1:1-10: ERROR: invalid character '$' uprobe:$-1 { 1 } ~~~~~~~~~ stdin:1:1-11: ERROR: syntax error, unexpected -, expecting { uprobe:$-1 { 1 } ~~~~~~~~~~ )"); test_parse_failure(bpftrace, R"(uprobe:$999999999999999999999999 { 1 })", R"( stdin:1:1-34: ERROR: param $999999999999999999999999 is out of integer range [1, 9223372036854775807] uprobe:$999999999999999999999999 { 1 } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, comment) { test("kprobe:f { /*** ***/0; }", "Program\n kprobe:f\n int: 0\n"); } TEST(Parser, map_assign) { test("kprobe:sys_open { @x = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " int: 1\n"); test("kprobe:sys_open { @x = @y; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " map: @y\n"); test("kprobe:sys_open { @x = arg0; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " builtin: arg0\n"); test("kprobe:sys_open { @x = count(); }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " call: count\n"); test("kprobe:sys_read { @x = sum(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: sum\n" " builtin: arg2\n"); test("kprobe:sys_read { @x = min(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: min\n" " builtin: arg2\n"); test("kprobe:sys_read { @x = max(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: max\n" " builtin: arg2\n"); test("kprobe:sys_read { @x = avg(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: avg\n" " builtin: arg2\n"); test("kprobe:sys_read { @x = stats(arg2); }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " call: stats\n" " builtin: arg2\n"); test("kprobe:sys_open { @x = \"mystring\" }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " string: mystring\n"); test("kprobe:sys_open { @x = $myvar; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " variable: $myvar\n"); } TEST(Parser, variable_assign) { test("kprobe:sys_open { $x = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " variable: $x\n" " int: 1\n"); test("kprobe:sys_open { $x = -1; }", "Program\n" " kprobe:sys_open\n" " =\n" " variable: $x\n" " int: -1\n"); char in_cstr[128]; char out_cstr[128]; snprintf(in_cstr, sizeof(in_cstr), "kprobe:sys_open { $x = %ld; }", LONG_MIN); snprintf(out_cstr, sizeof(out_cstr), "Program\n" " kprobe:sys_open\n" " =\n" " variable: $x\n" " int: %ld\n", LONG_MIN); test(std::string(in_cstr), std::string(out_cstr)); } TEST(semantic_analyser, compound_variable_assignments) { test("kprobe:f { $a = 0; $a <<= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " <<\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a >>= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " >>\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a += 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " +\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a -= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " -\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a *= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " *\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a /= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " /\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a %= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " %\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a &= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " &\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a |= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " |\n" " variable: $a\n" " int: 1\n"); test("kprobe:f { $a = 0; $a ^= 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " ^\n" " variable: $a\n" " int: 1\n"); } TEST(Parser, compound_variable_assignment_binary_expr) { test("kprobe:f { $a = 0; $a += 2 - 1 }", "Program\n" " kprobe:f\n" " =\n" " variable: $a\n" " int: 0\n" " =\n" " variable: $a\n" " +\n" " variable: $a\n" " -\n" " int: 2\n" " int: 1\n"); } TEST(Parser, compound_map_assignments) { test("kprobe:f { @a <<= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " <<\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a >>= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " >>\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a += 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " +\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a -= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " -\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a *= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " *\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a /= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " /\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a %= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " %\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a &= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " &\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a |= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " |\n" " map: @a\n" " int: 1\n"); test("kprobe:f { @a ^= 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " ^\n" " map: @a\n" " int: 1\n"); } TEST(Parser, compound_map_assignment_binary_expr) { test("kprobe:f { @a += 2 - 1 }", "Program\n" " kprobe:f\n" " =\n" " map: @a\n" " +\n" " map: @a\n" " -\n" " int: 2\n" " int: 1\n"); } TEST(Parser, integer_sizes) { test("kprobe:do_nanosleep { $x = 0x12345678; }", "Program\n" " kprobe:do_nanosleep\n" " =\n" " variable: $x\n" " int: 305419896\n"); test("kprobe:do_nanosleep { $x = 0x4444444412345678; }", "Program\n" " kprobe:do_nanosleep\n" " =\n" " variable: $x\n" " int: 4919131752149309048\n"); } TEST(Parser, map_key) { test("kprobe:sys_open { @x[0] = 1; @x[0,1,2] = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " int: 0\n" " int: 1\n" " =\n" " map: @x\n" " tuple:\n" " int: 0\n" " int: 1\n" " int: 2\n" " int: 1\n"); test("kprobe:sys_open { @x[(0,\"hi\",tid)] = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " tuple:\n" " int: 0\n" " string: hi\n" " builtin: tid\n" " int: 1\n"); test("kprobe:sys_open { @x[@a] = 1; @x[@a,@b,@c] = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " map: @a\n" " int: 1\n" " =\n" " map: @x\n" " tuple:\n" " map: @a\n" " map: @b\n" " map: @c\n" " int: 1\n"); test("kprobe:sys_open { @x[pid] = 1; @x[tid,uid,arg9] = 1; }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " builtin: pid\n" " int: 1\n" " =\n" " map: @x\n" " tuple:\n" " builtin: tid\n" " builtin: uid\n" " builtin: arg9\n" " int: 1\n"); } TEST(Parser, predicate) { test("kprobe:sys_open / @x / { 1; }", "Program\n" " kprobe:sys_open\n" " pred\n" " map: @x\n" " int: 1\n"); } TEST(Parser, predicate_containing_division) { test("kprobe:sys_open /100/25/ { 1; }", "Program\n" " kprobe:sys_open\n" " pred\n" " /\n" " int: 100\n" " int: 25\n" " int: 1\n"); } TEST(Parser, expressions) { test("kprobe:sys_open / 1 <= 2 && (9 - 4 != 5*10 || ~0) || comm == " "\"string\" /\n" "{\n" " 1;\n" "}", "Program\n" " kprobe:sys_open\n" " pred\n" " ||\n" " &&\n" " <=\n" " int: 1\n" " int: 2\n" " ||\n" " !=\n" " -\n" " int: 9\n" " int: 4\n" " *\n" " int: 5\n" " int: 10\n" " ~\n" " int: 0\n" " ==\n" " builtin: comm\n" " string: string\n" " int: 1\n"); } TEST(Parser, variable_post_increment_decrement) { test("kprobe:sys_open { $x++; }", "Program\n" " kprobe:sys_open\n" " ++ (post)\n" " variable: $x\n"); test("kprobe:sys_open { ++$x; }", "Program\n" " kprobe:sys_open\n" " ++ (pre)\n" " variable: $x\n"); test("kprobe:sys_open { $x--; }", "Program\n" " kprobe:sys_open\n" " -- (post)\n" " variable: $x\n"); test("kprobe:sys_open { --$x; }", "Program\n" " kprobe:sys_open\n" " -- (pre)\n" " variable: $x\n"); } TEST(Parser, map_increment_decrement) { test("kprobe:sys_open { @x++; }", "Program\n" " kprobe:sys_open\n" " ++ (post)\n" " map: @x\n"); test("kprobe:sys_open { ++@x; }", "Program\n" " kprobe:sys_open\n" " ++ (pre)\n" " map: @x\n"); test("kprobe:sys_open { @x--; }", "Program\n" " kprobe:sys_open\n" " -- (post)\n" " map: @x\n"); test("kprobe:sys_open { --@x; }", "Program\n" " kprobe:sys_open\n" " -- (pre)\n" " map: @x\n"); } TEST(Parser, bit_shifting) { test("kprobe:do_nanosleep { @x = 1 << 10 }", "Program\n" " kprobe:do_nanosleep\n" " =\n" " map: @x\n" " <<\n" " int: 1\n" " int: 10\n"); test("kprobe:do_nanosleep { @x = 1024 >> 9 }", "Program\n" " kprobe:do_nanosleep\n" " =\n" " map: @x\n" " >>\n" " int: 1024\n" " int: 9\n"); test("kprobe:do_nanosleep / 2 < 1 >> 8 / { $x = 1 }", "Program\n" " kprobe:do_nanosleep\n" " pred\n" " <\n" " int: 2\n" " >>\n" " int: 1\n" " int: 8\n" " =\n" " variable: $x\n" " int: 1\n"); } TEST(Parser, ternary_int) { test("kprobe:sys_open { @x = pid < 10000 ? 1 : 2 }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " ?:\n" " <\n" " builtin: pid\n" " int: 10000\n" " int: 1\n" " int: 2\n"); } TEST(Parser, if_block) { test("kprobe:sys_open { if (pid > 10000) { printf(\"%d is high\\n\", pid); } " "}", "Program\n" " kprobe:sys_open\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " call: printf\n" " string: %d is high\\n\n" " builtin: pid\n"); } TEST(Parser, if_stmt_if) { test("kprobe:sys_open { if (pid > 10000) { printf(\"%d is high\\n\", pid); } " "@pid = pid; if (pid < 1000) { printf(\"%d is low\\n\", pid); } }", "Program\n" " kprobe:sys_open\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " call: printf\n" " string: %d is high\\n\n" " builtin: pid\n" " =\n" " map: @pid\n" " builtin: pid\n" " if\n" " <\n" " builtin: pid\n" " int: 1000\n" " then\n" " call: printf\n" " string: %d is low\\n\n" " builtin: pid\n"); } TEST(Parser, if_block_variable) { test("kprobe:sys_open { if (pid > 10000) { $s = 10; } }", "Program\n" " kprobe:sys_open\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " int: 10\n"); } TEST(Parser, if_else) { test("kprobe:sys_open { if (pid > 10000) { $s = \"a\"; } else { $s= \"b\"; } " "printf(\"%d is high\\n\", pid, $s); }", "Program\n" " kprobe:sys_open\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " string: a\n" " else\n" " =\n" " variable: $s\n" " string: b\n" " call: printf\n" " string: %d is high\\n\n" " builtin: pid\n" " variable: $s\n"); } TEST(Parser, if_elseif) { test("kprobe:f { if (pid > 10000) { $s = 10; } else if (pid < 10) { $s = 2; " "} }", "Program\n" " kprobe:f\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " int: 10\n" " else\n" " if\n" " <\n" " builtin: pid\n" " int: 10\n" " then\n" " =\n" " variable: $s\n" " int: 2\n"); } TEST(Parser, if_elseif_else) { test("kprobe:f { if (pid > 10000) { $s = 10; } else if (pid < 10) { $s = 2; " "} else { $s = 1 } }", "Program\n" " kprobe:f\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " int: 10\n" " else\n" " if\n" " <\n" " builtin: pid\n" " int: 10\n" " then\n" " =\n" " variable: $s\n" " int: 2\n" " else\n" " =\n" " variable: $s\n" " int: 1\n"); } TEST(Parser, if_elseif_elseif_else) { test("kprobe:f { if (pid > 10000) { $s = 10; } else if (pid < 10) { $s = 2; " "} else if (pid > 999999) { $s = 0 } else { $s = 1 } }", "Program\n" " kprobe:f\n" " if\n" " >\n" " builtin: pid\n" " int: 10000\n" " then\n" " =\n" " variable: $s\n" " int: 10\n" " else\n" " if\n" " <\n" " builtin: pid\n" " int: 10\n" " then\n" " =\n" " variable: $s\n" " int: 2\n" " else\n" " if\n" " >\n" " builtin: pid\n" " int: 999999\n" " then\n" " =\n" " variable: $s\n" " int: 0\n" " else\n" " =\n" " variable: $s\n" " int: 1\n"); } TEST(Parser, unroll) { test("kprobe:sys_open { $i = 0; unroll(5) { printf(\"i: %d\\n\", $i); $i = " "$i + 1; } }", "Program\n" " kprobe:sys_open\n" " =\n" " variable: $i\n" " int: 0\n" " unroll\n" " int: 5\n" " block\n" " call: printf\n" " string: i: %d\\n\n" " variable: $i\n" " =\n" " variable: $i\n" " +\n" " variable: $i\n" " int: 1\n"); } TEST(Parser, ternary_str) { test(R"(kprobe:sys_open { @x = pid < 10000 ? "lo" : "high" })", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " ?:\n" " <\n" " builtin: pid\n" " int: 10000\n" " string: lo\n" " string: high\n"); } TEST(Parser, ternary_nested) { test("kprobe:sys_open { @x = pid < 10000 ? pid < 5000 ? 1 : 2 : 3 }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " ?:\n" " <\n" " builtin: pid\n" " int: 10000\n" " ?:\n" " <\n" " builtin: pid\n" " int: 5000\n" " int: 1\n" " int: 2\n" " int: 3\n"); } TEST(Parser, call) { test("kprobe:sys_open { @x = count(); @y = hist(1,2,3); delete(@x); }", "Program\n" " kprobe:sys_open\n" " =\n" " map: @x\n" " call: count\n" " =\n" " map: @y\n" " call: hist\n" " int: 1\n" " int: 2\n" " int: 3\n" " call: delete\n" " map: @x\n"); } TEST(Parser, call_unknown_function) { test_parse_failure("kprobe:sys_open { myfunc() }", R"( stdin:1:19-25: ERROR: Unknown function: myfunc kprobe:sys_open { myfunc() } ~~~~~~ )"); test_parse_failure("k:f { probe(); }", R"( stdin:1:7-12: ERROR: Unknown function: probe k:f { probe(); } ~~~~~ )"); } TEST(Parser, call_builtin) { // Builtins should not be usable as function test_parse_failure("k:f { probe(\"blah\"); }", R"( stdin:1:7-12: ERROR: Unknown function: probe k:f { probe("blah"); } ~~~~~ )"); test_parse_failure("k:f { probe(); }", R"( stdin:1:7-12: ERROR: Unknown function: probe k:f { probe(); } ~~~~~ )"); test_parse_failure("k:f { probe(123); }", R"( stdin:1:7-12: ERROR: Unknown function: probe k:f { probe(123); } ~~~~~ )"); } TEST(Parser, call_kaddr) { test("kprobe:f { @ = kaddr(\"avenrun\") }", "Program\n" " kprobe:f\n" " =\n" " map: @\n" " call: kaddr\n" " string: avenrun\n"); } TEST(Parser, multiple_probes) { test("kprobe:sys_open { 1; } kretprobe:sys_open { 2; }", "Program\n" " kprobe:sys_open\n" " int: 1\n" " kretprobe:sys_open\n" " int: 2\n"); } TEST(Parser, uprobe) { test("uprobe:/my/program:func { 1; }", "Program\n" " uprobe:/my/program:func\n" " int: 1\n"); test("uprobe:/my/go/program:\"pkg.func\u2C51\" { 1; }", "Program\n" " uprobe:/my/go/program:pkg.func\u2C51\n" " int: 1\n"); test("uprobe:/with#hash:asdf { 1 }", "Program\n" " uprobe:/with#hash:asdf\n" " int: 1\n"); test("uprobe:/my/program:1234 { 1; }", "Program\n" " uprobe:/my/program:1234\n" " int: 1\n"); // Trailing alnum chars are allowed (turns the entire arg into a symbol name) test("uprobe:/my/program:1234abc { 1; }", "Program\n" " uprobe:/my/program:1234abc\n" " int: 1\n"); // Test `:`s in quoted string test("uprobe:/my/program:\"A::f\" { 1; }", "Program\n" " uprobe:/my/program:A::f\n" " int: 1\n"); // Language prefix test("uprobe:/my/program:cpp:func { 1; }", "Program\n" " uprobe:/my/program:cpp:func\n" " int: 1\n"); test("uprobe:/my/dir+/program:1234abc { 1; }", "Program\n" " uprobe:/my/dir+/program:1234abc\n" " int: 1\n"); test_parse_failure("uprobe:f { 1 }", R"( stdin:1:1-9: ERROR: uprobe probe type requires 2 or 3 arguments, found 1 uprobe:f { 1 } ~~~~~~~~ )"); test_parse_failure("uprobe { 1 }", R"( stdin:1:1-7: ERROR: uprobe probe type requires 2 or 3 arguments, found 0 uprobe { 1 } ~~~~~~ )"); test_parse_failure("uprobe:/my/program*:0x1234 { 1 }", R"( stdin:1:1-27: ERROR: Cannot use wildcards with absolute address uprobe:/my/program*:0x1234 { 1 } ~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, usdt) { test("usdt:/my/program:probe { 1; }", "Program\n" " usdt:/my/program:probe\n" " int: 1\n"); // Without the escapes needed for C++ to compile: // usdt:/my/program:"\"probe\"" { 1; } // test(R"(usdt:/my/program:"\"probe\"" { 1; })", "Program\n" " usdt:/my/program:\"probe\"\n" " int: 1\n"); test_parse_failure("usdt { 1 }", R"( stdin:1:1-5: ERROR: usdt probe type requires 2 or 3 arguments, found 0 usdt { 1 } ~~~~ )"); } TEST(Parser, usdt_namespaced_probe) { test("usdt:/my/program:namespace:probe { 1; }", "Program\n" " usdt:/my/program:namespace:probe\n" " int: 1\n"); test("usdt:/my/program*:namespace:probe { 1; }", "Program\n" " usdt:/my/program*:namespace:probe\n" " int: 1\n"); test("usdt:/my/*program:namespace:probe { 1; }", "Program\n" " usdt:/my/*program:namespace:probe\n" " int: 1\n"); test("usdt:*my/program*:namespace:probe { 1; }", "Program\n" " usdt:*my/program*:namespace:probe\n" " int: 1\n"); } TEST(Parser, escape_chars) { test("kprobe:sys_open { \"newline\\nand " "tab\\tcr\\rbackslash\\\\quote\\\"here oct\\1009hex\\x309\" }", "Program\n" " kprobe:sys_open\n" " string: newline\\nand tab\\tcr\\rbackslash\\\\quote\\\"here " "oct@9hex09\n"); } TEST(Parser, begin_probe) { test("BEGIN { 1 }", "Program\n" " BEGIN\n" " int: 1\n"); test_parse_failure("BEGIN:f { 1 }", R"( stdin:1:1-8: ERROR: BEGIN probe type requires 0 arguments, found 1 BEGIN:f { 1 } ~~~~~~~ )"); test_parse_failure("BEGIN:path:f { 1 }", R"( stdin:1:1-13: ERROR: BEGIN probe type requires 0 arguments, found 2 BEGIN:path:f { 1 } ~~~~~~~~~~~~ )"); } TEST(Parser, end_probe) { test("END { 1 }", "Program\n" " END\n" " int: 1\n"); test_parse_failure("END:f { 1 }", R"( stdin:1:1-6: ERROR: END probe type requires 0 arguments, found 1 END:f { 1 } ~~~~~ )"); test_parse_failure("END:path:f { 1 }", R"( stdin:1:1-11: ERROR: END probe type requires 0 arguments, found 2 END:path:f { 1 } ~~~~~~~~~~ )"); } TEST(Parser, tracepoint_probe) { test("tracepoint:sched:sched_switch { 1 }", "Program\n" " tracepoint:sched:sched_switch\n" " int: 1\n"); test("tracepoint:* { 1 }", "Program\n" " tracepoint:*:*\n" " int: 1\n"); test_parse_failure("tracepoint:f { 1 }", R"( stdin:1:1-13: ERROR: tracepoint probe type requires 2 arguments, found 1 tracepoint:f { 1 } ~~~~~~~~~~~~ )"); test_parse_failure("tracepoint { 1 }", R"( stdin:1:1-11: ERROR: tracepoint probe type requires 2 arguments, found 0 tracepoint { 1 } ~~~~~~~~~~ )"); } TEST(Parser, profile_probe) { test("profile:ms:997 { 1 }", "Program\n" " profile:ms:997\n" " int: 1\n"); test_parse_failure("profile:ms:nan { 1 }", R"( stdin:1:1-15: ERROR: stoull Invalid rate of profile probe profile:ms:nan { 1 } ~~~~~~~~~~~~~~ )"); test_parse_failure("profile:f { 1 }", R"( stdin:1:1-10: ERROR: profile probe type requires 2 arguments, found 1 profile:f { 1 } ~~~~~~~~~ )"); test_parse_failure("profile { 1 }", R"( stdin:1:1-8: ERROR: profile probe type requires 2 arguments, found 0 profile { 1 } ~~~~~~~ )"); test_parse_failure("profile:s:1b { 1 }", R"( stdin:1:1-13: ERROR: Found trailing non-numeric characters Invalid rate of profile probe profile:s:1b { 1 } ~~~~~~~~~~~~ )"); } TEST(Parser, interval_probe) { test("interval:s:1 { 1 }", "Program\n" " interval:s:1\n" " int: 1\n"); test("interval:s:1e3 { 1 }", "Program\n" " interval:s:1000\n" " int: 1\n"); test("interval:s:1_0_0_0 { 1 }", "Program\n" " interval:s:1000\n" " int: 1\n"); test_parse_failure("interval:s:1b { 1 }", R"( stdin:1:1-14: ERROR: Found trailing non-numeric characters Invalid rate of interval probe interval:s:1b { 1 } ~~~~~~~~~~~~~ )"); } TEST(Parser, software_probe) { test("software:faults:1000 { 1 }", "Program\n" " software:faults:1000\n" " int: 1\n"); test("software:faults:1e3 { 1 }", "Program\n" " software:faults:1000\n" " int: 1\n"); test("software:faults:1_000 { 1 }", "Program\n" " software:faults:1000\n" " int: 1\n"); test_parse_failure("software:faults:1b { 1 }", R"( stdin:1:1-19: ERROR: Found trailing non-numeric characters Invalid count for software probe software:faults:1b { 1 } ~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, hardware_probe) { test("hardware:cache-references:1000000 { 1 }", "Program\n" " hardware:cache-references:1000000\n" " int: 1\n"); test("hardware:cache-references:1e6 { 1 }", "Program\n" " hardware:cache-references:1000000\n" " int: 1\n"); test("hardware:cache-references:1_000_000 { 1 }", "Program\n" " hardware:cache-references:1000000\n" " int: 1\n"); test_parse_failure("hardware:cache-references:1b { 1 }", R"( stdin:1:1-29: ERROR: Found trailing non-numeric characters Invalid count for hardware probe hardware:cache-references:1b { 1 } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, watchpoint_probe) { test("watchpoint:1234:8:w { 1 }", "Program\n" " watchpoint:1234:8:w\n" " int: 1\n"); test_parse_failure("watchpoint:1b:8:w { 1 }", R"( stdin:1:1-18: ERROR: Found trailing non-numeric characters Invalid function/address argument watchpoint:1b:8:w { 1 } ~~~~~~~~~~~~~~~~~ )"); test_parse_failure("watchpoint:1:8a:w { 1 }", R"( stdin:1:1-18: ERROR: Found trailing non-numeric characters Invalid length argument watchpoint:1:8a:w { 1 } ~~~~~~~~~~~~~~~~~ )"); test_parse_failure("watchpoint:1b:8a:w { 1 }", R"( stdin:1:1-19: ERROR: Found trailing non-numeric characters Invalid function/address argument watchpoint:1b:8a:w { 1 } ~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("watchpoint:+arg0:8:rw { 1 }", R"( stdin:1:1-22: ERROR: Invalid function/address argument watchpoint:+arg0:8:rw { 1 } ~~~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("watchpoint:func1:8:rw { 1 }", R"( stdin:1:1-22: ERROR: stoull Invalid function/address argument watchpoint:func1:8:rw { 1 } ~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, asyncwatchpoint_probe) { test("asyncwatchpoint:1234:8:w { 1 }", "Program\n" " asyncwatchpoint:1234:8:w\n" " int: 1\n"); test_parse_failure("asyncwatchpoint:1b:8:w { 1 }", R"( stdin:1:1-23: ERROR: Found trailing non-numeric characters Invalid function/address argument asyncwatchpoint:1b:8:w { 1 } ~~~~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("asyncwatchpoint:1:8a:w { 1 }", R"( stdin:1:1-23: ERROR: Found trailing non-numeric characters Invalid length argument asyncwatchpoint:1:8a:w { 1 } ~~~~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("asyncwatchpoint:1b:8a:w { 1 }", R"( stdin:1:1-24: ERROR: Found trailing non-numeric characters Invalid function/address argument asyncwatchpoint:1b:8a:w { 1 } ~~~~~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("asyncwatchpoint:+arg0:8:rw { 1 }", R"( stdin:1:1-27: ERROR: Invalid function/address argument asyncwatchpoint:+arg0:8:rw { 1 } ~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("asyncwatchpoint:func1:8:rw { 1 }", R"( stdin:1:1-27: ERROR: stoull Invalid function/address argument asyncwatchpoint:func1:8:rw { 1 } ~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, multiple_attach_points_kprobe) { test("BEGIN,kprobe:sys_open,uprobe:/bin/" "sh:foo,tracepoint:syscalls:sys_enter_* { 1 }", "Program\n" " BEGIN\n" " kprobe:sys_open\n" " uprobe:/bin/sh:foo\n" " tracepoint:syscalls:sys_enter_*\n" " int: 1\n"); } TEST(Parser, character_class_attach_point) { test("kprobe:[Ss]y[Ss]_read { 1 }", "Program\n" " kprobe:[Ss]y[Ss]_read\n" " int: 1\n"); } TEST(Parser, wildcard_probetype) { test("t*point:sched:sched_switch { 1; }", "Program\n" " tracepoint:sched:sched_switch\n" " int: 1\n"); test("*ware:* { 1; }", "Program\n" " hardware:*\n" " software:*\n" " int: 1\n"); test("*:/bin/sh:* { 1; }", "Program\n" " uprobe:/bin/sh:*\n" " usdt:/bin/sh:*\n" " int: 1\n"); test_parse_failure("iter:task* { }", R"( stdin:1:1-11: ERROR: iter probe type does not support wildcards iter:task* { } ~~~~~~~~~~ )"); } TEST(Parser, wildcard_attach_points) { test("kprobe:sys_* { 1 }", "Program\n" " kprobe:sys_*\n" " int: 1\n"); test("kprobe:*blah { 1 }", "Program\n" " kprobe:*blah\n" " int: 1\n"); test("kprobe:sys*blah { 1 }", "Program\n" " kprobe:sys*blah\n" " int: 1\n"); test("kprobe:* { 1 }", "Program\n" " kprobe:*\n" " int: 1\n"); test("kprobe:sys_* { @x = cpu*retval }", "Program\n" " kprobe:sys_*\n" " =\n" " map: @x\n" " *\n" " builtin: cpu\n" " builtin: retval\n"); test("kprobe:sys_* { @x = *arg0 }", "Program\n" " kprobe:sys_*\n" " =\n" " map: @x\n" " dereference\n" " builtin: arg0\n"); } TEST(Parser, wildcard_path) { test("uprobe:/my/program*:* { 1; }", "Program\n" " uprobe:/my/program*:*\n" " int: 1\n"); test("uprobe:/my/program*:func { 1; }", "Program\n" " uprobe:/my/program*:func\n" " int: 1\n"); test("uprobe:*my/program*:func { 1; }", "Program\n" " uprobe:*my/program*:func\n" " int: 1\n"); test("uprobe:/my/program*foo:func { 1; }", "Program\n" " uprobe:/my/program*foo:func\n" " int: 1\n"); test("usdt:/my/program*:* { 1; }", "Program\n" " usdt:/my/program*:*\n" " int: 1\n"); test("usdt:/my/program*:func { 1; }", "Program\n" " usdt:/my/program*:func\n" " int: 1\n"); test("usdt:*my/program*:func { 1; }", "Program\n" " usdt:*my/program*:func\n" " int: 1\n"); test("usdt:/my/program*foo:func { 1; }", "Program\n" " usdt:/my/program*foo:func\n" " int: 1\n"); // Make sure calls or builtins don't cause issues test("usdt:/my/program*avg:func { 1; }", "Program\n" " usdt:/my/program*avg:func\n" " int: 1\n"); test("usdt:/my/program*nsecs:func { 1; }", "Program\n" " usdt:/my/program*nsecs:func\n" " int: 1\n"); } TEST(Parser, dot_in_func) { test("uprobe:/my/go/program:runtime.main.func1 { 1; }", "Program\n" " uprobe:/my/go/program:runtime.main.func1\n" " int: 1\n"); } TEST(Parser, wildcard_func) { test("usdt:/my/program:abc*cd { 1; }", "Program\n" " usdt:/my/program:abc*cd\n" " int: 1\n"); test("usdt:/my/program:abc*c*d { 1; }", "Program\n" " usdt:/my/program:abc*c*d\n" " int: 1\n"); std::string keywords[] = { "arg0", "args", "curtask", "func", "gid" "rand", "uid", "avg", "cat", "exit", "kaddr", "min", "printf", "usym", "kstack", "ustack", "bpftrace", "perf", "raw", "uprobe", "kprobe", }; for (auto kw : keywords) { test("usdt:/my/program:" + kw + "*c*d { 1; }", "Program\n" " usdt:/my/program:" + kw + "*c*d\n" " int: 1\n"); test("usdt:/my/program:abc*" + kw + "*c*d { 1; }", "Program\n" " usdt:/my/program:abc*" + kw + "*c*d\n" " int: 1\n"); } } TEST(Parser, short_map_name) { test("kprobe:sys_read { @ = 1 }", "Program\n" " kprobe:sys_read\n" " =\n" " map: @\n" " int: 1\n"); } TEST(Parser, include) { test("#include \nkprobe:sys_read { @x = 1 }", "#include \n" "\n" "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " int: 1\n"); } TEST(Parser, include_quote) { test("#include \"stdio.h\"\nkprobe:sys_read { @x = 1 }", "#include \"stdio.h\"\n" "\n" "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " int: 1\n"); } TEST(Parser, include_multiple) { test("#include \n#include \"blah\"\n#include " "\nkprobe:sys_read { @x = 1 }", "#include \n" "#include \"blah\"\n" "#include \n" "\n" "Program\n" " kprobe:sys_read\n" " =\n" " map: @x\n" " int: 1\n"); } TEST(Parser, brackets) { test("kprobe:sys_read { (arg0*arg1) }", "Program\n" " kprobe:sys_read\n" " *\n" " builtin: arg0\n" " builtin: arg1\n"); } TEST(Parser, cast_simple_type) { test("kprobe:sys_read { (int32)arg0; }", "Program\n" " kprobe:sys_read\n" " (int32)\n" " builtin: arg0\n"); } TEST(Parser, cast_simple_type_pointer) { test("kprobe:sys_read { (int32 *)arg0; }", "Program\n" " kprobe:sys_read\n" " (int32 *)\n" " builtin: arg0\n"); } TEST(Parser, cast_sized_type) { test("kprobe:sys_read { (string)arg0; }", "Program\n" " kprobe:sys_read\n" " (string[0])\n" " builtin: arg0\n"); } TEST(Parser, cast_sized_type_pointer) { test("kprobe:sys_read { (string *)arg0; }", "Program\n" " kprobe:sys_read\n" " (string[0] *)\n" " builtin: arg0\n"); } TEST(Parser, cast_sized_type_pointer_with_size) { test("kprobe:sys_read { (string[1] *)arg0; }", "Program\n" " kprobe:sys_read\n" " (string[1] *)\n" " builtin: arg0\n"); } TEST(Parser, cast_struct) { test("kprobe:sys_read { (struct mytype)arg0; }", "Program\n" " kprobe:sys_read\n" " (struct mytype)\n" " builtin: arg0\n"); test("kprobe:sys_read { (union mytype)arg0; }", "Program\n" " kprobe:sys_read\n" " (union mytype)\n" " builtin: arg0\n"); } TEST(Parser, cast_struct_ptr) { test("kprobe:sys_read { (struct mytype*)arg0; }", "Program\n" " kprobe:sys_read\n" " (struct mytype *)\n" " builtin: arg0\n"); test("kprobe:sys_read { (union mytype*)arg0; }", "Program\n" " kprobe:sys_read\n" " (union mytype *)\n" " builtin: arg0\n"); } TEST(Parser, cast_typedef) { test("kprobe:sys_read { (mytype)arg0; }", "Program\n" " kprobe:sys_read\n" " (mytype)\n" " builtin: arg0\n"); } TEST(Parser, cast_ptr_typedef) { test("kprobe:sys_read { (mytype*)arg0; }", "Program\n" " kprobe:sys_read\n" " (mytype *)\n" " builtin: arg0\n"); } TEST(Parser, cast_multiple_pointer) { test("kprobe:sys_read { (int32 *****)arg0; }", "Program\n" " kprobe:sys_read\n" " (int32 * * * * *)\n" " builtin: arg0\n"); } TEST(Parser, cast_or_expr1) { test("kprobe:sys_read { (struct mytype)*arg0; }", "Program\n" " kprobe:sys_read\n" " (struct mytype)\n" " dereference\n" " builtin: arg0\n"); } TEST(Parser, cast_or_expr2) { test("kprobe:sys_read { (arg1)*arg0; }", "Program\n" " kprobe:sys_read\n" " *\n" " builtin: arg1\n" " builtin: arg0\n"); } TEST(Parser, cast_precedence) { test("kprobe:sys_read { (struct mytype)arg0.field; }", "Program\n" " kprobe:sys_read\n" " (struct mytype)\n" " .\n" " builtin: arg0\n" " field\n"); test("kprobe:sys_read { (struct mytype*)arg0->field; }", "Program\n" " kprobe:sys_read\n" " (struct mytype *)\n" " .\n" " dereference\n" " builtin: arg0\n" " field\n"); test("kprobe:sys_read { (struct mytype)arg0+123; }", "Program\n" " kprobe:sys_read\n" " +\n" " (struct mytype)\n" " builtin: arg0\n" " int: 123\n"); } TEST(Parser, cast_enum) { test("enum Foo { ONE = 1 } kprobe:sys_read { (enum Foo)1; }", "enum Foo { ONE = 1 };\n" "\n" "Program\n" " kprobe:sys_read\n" " (enum Foo)\n" " int: 1\n"); } TEST(Parser, sizeof_expression) { test("kprobe:sys_read { sizeof(arg0); }", "Program\n" " kprobe:sys_read\n" " sizeof: \n" " builtin: arg0\n"); } TEST(Parser, sizeof_type) { test("kprobe:sys_read { sizeof(int32); }", "Program\n" " kprobe:sys_read\n" " sizeof: \n"); } TEST(Parser, offsetof_type) { test("struct Foo { int x; } BEGIN { offsetof(struct Foo, x); }", "struct Foo { int x; };\n" "\n" "Program\n" " BEGIN\n" " offsetof: \n" " struct Foo\n" " x\n"); test("struct Foo { struct Bar { int x; } bar; } " "BEGIN { offsetof(struct Foo, bar.x); }", "struct Foo { struct Bar { int x; } bar; };\n" "\n" "Program\n" " BEGIN\n" " offsetof: \n" " struct Foo\n" " bar\n" " x\n"); test_parse_failure("struct Foo { struct Bar { int x; } *bar; } " "BEGIN { offsetof(struct Foo, bar->x); }", R"( stdin:1:74-79: ERROR: syntax error, unexpected ->, expecting ) or . struct Foo { struct Bar { int x; } *bar; } BEGIN { offsetof(struct Foo, bar->x); } ~~~~~ )"); } TEST(Parser, offsetof_expression) { test("struct Foo { int x; }; " "BEGIN { $foo = (struct Foo *)0; offsetof(*$foo, x); }", "struct Foo { int x; };;\n" "\n" "Program\n" " BEGIN\n" " =\n" " variable: $foo\n" " (struct Foo *)\n" " int: 0\n" " offsetof: \n" " dereference\n" " variable: $foo\n" " x\n"); } TEST(Parser, offsetof_builtin_type) { test("struct Foo { timestamp x; } BEGIN { offsetof(struct Foo, timestamp); }", "struct Foo { timestamp x; };\n" "\n" "Program\n" " BEGIN\n" " offsetof: \n" " struct Foo\n" " timestamp\n"); } TEST(Parser, dereference_precedence) { test("kprobe:sys_read { *@x+1 }", "Program\n" " kprobe:sys_read\n" " +\n" " dereference\n" " map: @x\n" " int: 1\n"); test("kprobe:sys_read { *@x**@y }", "Program\n" " kprobe:sys_read\n" " *\n" " dereference\n" " map: @x\n" " dereference\n" " map: @y\n"); test("kprobe:sys_read { *@x*@y }", "Program\n" " kprobe:sys_read\n" " *\n" " dereference\n" " map: @x\n" " map: @y\n"); test("kprobe:sys_read { *@x.myfield }", "Program\n" " kprobe:sys_read\n" " dereference\n" " .\n" " map: @x\n" " myfield\n"); } TEST(Parser, field_access) { test("kprobe:sys_read { @x.myfield; }", "Program\n" " kprobe:sys_read\n" " .\n" " map: @x\n" " myfield\n"); test("kprobe:sys_read { @x->myfield; }", "Program\n" " kprobe:sys_read\n" " .\n" " dereference\n" " map: @x\n" " myfield\n"); } TEST(Parser, field_access_builtin) { test("kprobe:sys_read { @x.count; }", "Program\n" " kprobe:sys_read\n" " .\n" " map: @x\n" " count\n"); test("kprobe:sys_read { @x->count; }", "Program\n" " kprobe:sys_read\n" " .\n" " dereference\n" " map: @x\n" " count\n"); } TEST(Parser, field_access_builtin_type) { test("kprobe:sys_read { @x.timestamp; }", "Program\n" " kprobe:sys_read\n" " .\n" " map: @x\n" " timestamp\n"); test("kprobe:sys_read { @x->timestamp; }", "Program\n" " kprobe:sys_read\n" " .\n" " dereference\n" " map: @x\n" " timestamp\n"); } TEST(Parser, array_access) { test("kprobe:sys_read { x[index]; }", "Program\n" " kprobe:sys_read\n" " []\n" " identifier: x\n" " identifier: index\n"); test("kprobe:sys_read { $val = x[index]; }", "Program\n" " kprobe:sys_read\n" " =\n" " variable: $val\n" " []\n" " identifier: x\n" " identifier: index\n"); } TEST(Parser, cstruct) { test("struct Foo { int x, y; char *str; } kprobe:sys_read { 1; }", "struct Foo { int x, y; char *str; };\n" "\n" "Program\n" " kprobe:sys_read\n" " int: 1\n"); } TEST(Parser, cstruct_semicolon) { test("struct Foo { int x, y; char *str; }; kprobe:sys_read { 1; }", "struct Foo { int x, y; char *str; };;\n" "\n" "Program\n" " kprobe:sys_read\n" " int: 1\n"); } TEST(Parser, cstruct_nested) { test("struct Foo { struct { int x; } bar; } kprobe:sys_read { 1; }", "struct Foo { struct { int x; } bar; };\n" "\n" "Program\n" " kprobe:sys_read\n" " int: 1\n"); } TEST(Parser, unexpected_symbol) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_EQ(driver.parse_str("i:s:1 { < }"), 1); std::string expected = R"(stdin:1:9-10: ERROR: syntax error, unexpected < i:s:1 { < } ~ )"; EXPECT_EQ(out.str(), expected); } TEST(Parser, string_with_tab) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_EQ(driver.parse_str("i:s:1\t\t\t$a"), 1); std::string expected = R"(stdin:1:9-11: ERROR: syntax error, unexpected variable, expecting { i:s:1 $a ~~ )"; EXPECT_EQ(out.str(), expected); } TEST(Parser, unterminated_string) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_EQ(driver.parse_str("kprobe:f { \"asdf }"), 1); std::string expected = R"(stdin:1:12-19: ERROR: unterminated string kprobe:f { "asdf } ~~~~~~~ stdin:1:12-19: ERROR: syntax error, unexpected end of file kprobe:f { "asdf } ~~~~~~~ )"; EXPECT_EQ(out.str(), expected); } TEST(Parser, kprobe_offset) { test("k:fn+1 {}", "Program\n" " kprobe:fn+1\n"); test("k:fn+0x10 {}", "Program\n" " kprobe:fn+16\n"); test("k:\"fn.abc\"+1 {}", "Program\n" " kprobe:fn.abc+1\n"); test("k:\"fn.abc\"+0x10 {}", "Program\n" " kprobe:fn.abc+16\n"); test_parse_failure("k:asdf+123abc", R"( stdin:1:1-14: ERROR: unexpected end of file, expected { k:asdf+123abc ~~~~~~~~~~~~~ )"); } TEST(Parser, kretprobe_offset) { // Not supported yet test_parse_failure("kr:fn+1 { 1 }", R"( stdin:1:1-8: ERROR: Offset not allowed kr:fn+1 { 1 } ~~~~~~~ )"); } TEST(Parser, uprobe_offset) { test("u:./test:fn+1 {}", "Program\n" " uprobe:./test:fn+1\n"); test("u:./test:fn+0x10 {}", "Program\n" " uprobe:./test:fn+16\n"); test("u:./test:\"fn.abc\"+1 {}", "Program\n" " uprobe:./test:fn.abc+1\n"); test("u:./test:\"fn.abc\"+0x10 {}", "Program\n" " uprobe:./test:fn.abc+16\n"); } TEST(Parser, uretprobe_offset) { // Not supported yet test_parse_failure("ur:./test:fn+1 { 1 }", R"( stdin:1:1-15: ERROR: Offset not allowed ur:./test:fn+1 { 1 } ~~~~~~~~~~~~~~ )"); test_parse_failure("uretprobe:/bin/sh:f+0x10 { 1 }", R"( stdin:1:1-25: ERROR: Offset not allowed uretprobe:/bin/sh:f+0x10 { 1 } ~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, invalid_increment_decrement) { test_parse_failure("i:s:1 { @=5++}", R"( stdin:1:9-14: ERROR: syntax error, unexpected ++, expecting ; or } i:s:1 { @=5++} ~~~~~ )"); test_parse_failure("i:s:1 { @=++5}", R"( stdin:1:9-14: ERROR: syntax error, unexpected integer i:s:1 { @=++5} ~~~~~ )"); test_parse_failure("i:s:1 { @=5--}", R"( stdin:1:9-14: ERROR: syntax error, unexpected --, expecting ; or } i:s:1 { @=5--} ~~~~~ )"); test_parse_failure("i:s:1 { @=--5}", R"( stdin:1:9-14: ERROR: syntax error, unexpected integer i:s:1 { @=--5} ~~~~~ )"); test_parse_failure("i:s:1 { @=\"a\"++}", R"( stdin:1:9-16: ERROR: syntax error, unexpected ++, expecting ; or } i:s:1 { @="a"++} ~~~~~~~ )"); } TEST(Parser, long_param_overflow) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_NO_THROW( driver.parse_str("i:s:100 { @=$111111111111111111111111111 }")); std::string expected = "stdin:1:11-41: ERROR: param " "$111111111111111111111111111 is out of " "integer range [1, " + std::to_string(std::numeric_limits::max()) + "]\n" + "i:s:100 { @=$111111111111111111111111111 }\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; EXPECT_EQ(out.str(), expected); } TEST(Parser, empty_arguments) { test_parse_failure("::k::vfs_open:: { 1 }", R"( stdin:1:1-26: ERROR: No attach points for probe ::k::vfs_open:: { 1 } ~~~~~~~~~~~~~~~~~~~~~ )"); // Error location is incorrect: #3063 test_parse_failure("k:vfs_open:: { 1 }", R"( stdin:1:1-14: ERROR: kprobe probe type requires 1 or 2 arguments, found 3 k:vfs_open:: { 1 } ~~~~~~~~~~~~~ )"); test_parse_failure(":w:0x10000000:8:rw { 1 }", R"( stdin:1:1-25: ERROR: No attach points for probe :w:0x10000000:8:rw { 1 } ~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, int_notation) { test("k:f { print(1e6); }", "Program\n kprobe:f\n call: print\n int: 1000000\n"); test("k:f { print(5e9); }", "Program\n kprobe:f\n call: print\n int: 5000000000\n"); test("k:f { print(1e1_0); }", "Program\n kprobe:f\n call: print\n int: 10000000000\n"); test("k:f { print(1_000_000_000_0); }", "Program\n kprobe:f\n call: print\n int: 10000000000\n"); test("k:f { print(1_0_0_0_00_0_000_0); }", "Program\n kprobe:f\n call: print\n int: 10000000000\n"); test("k:f { print(123_456_789_0); }", "Program\n kprobe:f\n call: print\n int: 1234567890\n"); test("k:f { print(0xe5); }", "Program\n kprobe:f\n call: print\n int: 229\n"); test("k:f { print(0x5e5); }", "Program\n kprobe:f\n call: print\n int: 1509\n"); test("k:f { print(0xeeee); }", "Program\n kprobe:f\n call: print\n int: 61166\n"); test("k:f { print(0777); }", "Program\n kprobe:f\n call: print\n int: 511\n"); test("k:f { print(0123); }", "Program\n kprobe:f\n call: print\n int: 83\n"); test("k:f { print(1_000u); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test("k:f { print(1_000ul); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test("k:f { print(1_000ull); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test("k:f { print(1_000l); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test("k:f { print(1_000ll); }", "Program\n kprobe:f\n call: print\n int: 1000\n"); test_parse_failure("k:f { print(5e-9); }", R"( stdin:1:7-15: ERROR: syntax error, unexpected identifier, expecting ) or "," k:f { print(5e-9); } ~~~~~~~~ )"); test_parse_failure("k:f { print(1e17); }", R"( stdin:1:7-17: ERROR: Exponent will overflow integer range: 17 k:f { print(1e17); } ~~~~~~~~~~ )"); test_parse_failure("k:f { print(12e4); }", R"( stdin:1:7-17: ERROR: Coefficient part of scientific literal must be in range (0,9), got: 12 k:f { print(12e4); } ~~~~~~~~~~ )"); test_parse_failure("k:f { print(1_1e100); }", R"( stdin:1:7-20: ERROR: Coefficient part of scientific literal must be in range (0,9), got: 11 k:f { print(1_1e100); } ~~~~~~~~~~~~~ )"); test_parse_failure("k:f { print(1e1_1_); }", R"( stdin:1:7-19: ERROR: syntax error, unexpected identifier, expecting ) or "," k:f { print(1e1_1_); } ~~~~~~~~~~~~ )"); test_parse_failure("k:f { print(1_1_e100); }", R"( stdin:1:7-21: ERROR: syntax error, unexpected identifier, expecting ) or "," k:f { print(1_1_e100); } ~~~~~~~~~~~~~~ )"); test_parse_failure("k:f { print(1_1_); }", R"( stdin:1:7-17: ERROR: syntax error, unexpected identifier, expecting ) or "," k:f { print(1_1_); } ~~~~~~~~~~ )"); test_parse_failure("k:f { print(1ulll); }", R"( stdin:1:7-18: ERROR: syntax error, unexpected identifier, expecting ) or "," k:f { print(1ulll); } ~~~~~~~~~~~ )"); test_parse_failure("k:f { print(1lul); }", R"( stdin:1:7-17: ERROR: syntax error, unexpected identifier, expecting ) or "," k:f { print(1lul); } ~~~~~~~~~~ )"); } TEST(Parser, while_loop) { test("i:ms:100 { $a = 0; while($a < 10) { $a++ }}", R"PROG(Program interval:ms:100 = variable: $a int: 0 while( < variable: $a int: 10 ) ++ (post) variable: $a )PROG"); } TEST(Parser, tuple_assignment_error_message) { BPFtrace bpftrace; std::stringstream out; Driver driver(bpftrace, out); EXPECT_EQ(driver.parse_str("i:s:1 { @x = (1, 2); $x.1 = 1; }"), 1); std::string expected = R"(stdin:1:22-30: ERROR: Tuples are immutable once created. Consider creating a new tuple and assigning it instead. i:s:1 { @x = (1, 2); $x.1 = 1; } ~~~~~~~~ )"; EXPECT_EQ(out.str(), expected); } TEST(Parser, tuple_assignment_error) { test_parse_failure("i:s:1 { (1, 0) = 0 }", R"( stdin:1:16-17: ERROR: syntax error, unexpected =, expecting ; or } i:s:1 { (1, 0) = 0 } ~ )"); test_parse_failure("i:s:1 { ((1, 0), 3).0.0 = 3 }", R"( stdin:1:9-28: ERROR: Tuples are immutable once created. Consider creating a new tuple and assigning it instead. i:s:1 { ((1, 0), 3).0.0 = 3 } ~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("i:s:1 { ((1, 0), 3).0 = (0, 1) }", R"( stdin:1:9-31: ERROR: Tuples are immutable once created. Consider creating a new tuple and assigning it instead. i:s:1 { ((1, 0), 3).0 = (0, 1) } ~~~~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure(R"(i:s:1 { (1, "two", (3, 4)).5 = "six"; })", R"( stdin:1:9-37: ERROR: Tuples are immutable once created. Consider creating a new tuple and assigning it instead. i:s:1 { (1, "two", (3, 4)).5 = "six"; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("i:s:1 { $a = 1; $a.2 = 3 }", R"( stdin:1:17-25: ERROR: Tuples are immutable once created. Consider creating a new tuple and assigning it instead. i:s:1 { $a = 1; $a.2 = 3 } ~~~~~~~~ )"); test_parse_failure("i:s:1 { 0.1 = 1.0 }", R"( stdin:1:9-18: ERROR: Tuples are immutable once created. Consider creating a new tuple and assigning it instead. i:s:1 { 0.1 = 1.0 } ~~~~~~~~~ )"); } TEST(Parser, abs_knl_address) { char in_cstr[64]; char out_cstr[64]; snprintf(in_cstr, sizeof(in_cstr), "watchpoint:0x%lx:4:w { 1; }", ULONG_MAX); snprintf(out_cstr, sizeof(out_cstr), "Program\n" " watchpoint:%lu:4:w\n" " int: 1\n", ULONG_MAX); test(std::string(in_cstr), std::string(out_cstr)); } TEST(Parser, invalid_provider) { test_parse_failure("asdf { }", R"( stdin:1:1-5: ERROR: Invalid probe type: asdf asdf { } ~~~~ )"); test_parse_failure("asdf:xyz { }", R"( stdin:1:1-9: ERROR: Invalid probe type: asdf asdf:xyz { } ~~~~~~~~ )"); test_parse_failure("asdf:xyz:www { }", R"( stdin:1:1-13: ERROR: Invalid probe type: asdf asdf:xyz:www { } ~~~~~~~~~~~~ )"); } TEST(Parser, non_fatal_errors) { // The non-fatal error from parsing "Stream" as an integer should not be // displayed test_parse_failure("uprobe:asdf:Stream {} tracepoint:only_one_arg {}", R"( stdin:1:22-46: ERROR: tracepoint probe type requires 2 arguments, found 1 uprobe:asdf:Stream {} tracepoint:only_one_arg {} ~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, config) { test("config = { blah = 5 } BEGIN {}", R"( Program config = config var: blah int: 5 BEGIN )"); test("config = { blah = 5; } BEGIN {}", R"( Program config = config var: blah int: 5 BEGIN )"); test("config = { blah = 5; zoop = \"a\"; } BEGIN {}", R"( Program config = config var: blah int: 5 = config var: zoop string: a BEGIN )"); test("config = {} BEGIN {}", R"( Program config BEGIN )"); } TEST(Parser, config_error) { test_parse_failure("i:s:1 { exit(); } config = { BPFTRACE_STACK_MODE=perf }", R"( stdin:1:19-25: ERROR: syntax error, unexpected config, expecting { i:s:1 { exit(); } config = { BPFTRACE_STACK_MODE=perf } ~~~~~~ )"); test_parse_failure("config = { exit(); } i:s:1 { exit(); }", R"( stdin:1:12-16: ERROR: syntax error, unexpected call, expecting } or identifier config = { exit(); } i:s:1 { exit(); } ~~~~ )"); test_parse_failure("config = { @start = nsecs; } i:s:1 { exit(); }", R"( stdin:1:12-18: ERROR: syntax error, unexpected map, expecting } or identifier config = { @start = nsecs; } i:s:1 { exit(); } ~~~~~~ )"); test_parse_failure("BEGIN { @start = nsecs; } config = { " "BPFTRACE_STACK_MODE=perf } i:s:1 { exit(); }", R"( stdin:1:27-33: ERROR: syntax error, unexpected config, expecting { BEGIN { @start = nsecs; } config = { BPFTRACE_STACK_MODE=perf } i:s:1 { exit(); } ~~~~~~ )"); test_parse_failure("config = { BPFTRACE_STACK_MODE=perf " "BPFTRACE_MAX_PROBES=2 } i:s:1 { exit(); }", R"( stdin:1:37-56: ERROR: syntax error, unexpected identifier, expecting ; or } config = { BPFTRACE_STACK_MODE=perf BPFTRACE_MAX_PROBES=2 } i:s:1 { exit(); } ~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("config = { BPFTRACE_STACK_MODE=perf } i:s:1 { " "BPFTRACE_MAX_PROBES=2; exit(); }", R"( stdin:1:47-67: ERROR: syntax error, unexpected =, expecting ; or } config = { BPFTRACE_STACK_MODE=perf } i:s:1 { BPFTRACE_MAX_PROBES=2; exit(); } ~~~~~~~~~~~~~~~~~~~~ )"); test_parse_failure("config { BPFTRACE_STACK_MODE=perf } i:s:1 { exit(); }", R"( stdin:1:8-9: ERROR: syntax error, unexpected {, expecting = config { BPFTRACE_STACK_MODE=perf } i:s:1 { exit(); } ~ )"); test_parse_failure("BPFTRACE_STACK_MODE=perf; i:s:1 { exit(); }", R"( stdin:1:1-21: ERROR: syntax error, unexpected =, expecting { BPFTRACE_STACK_MODE=perf; i:s:1 { exit(); } ~~~~~~~~~~~~~~~~~~~~ )"); } TEST(Parser, keywords_as_identifiers) { std::vector keywords = { "break", "config", "continue", "else", "for", "if", "offsetof", "return", "sizeof", "unroll", "while" }; for (const auto &keyword : keywords) { test("BEGIN { $x = (struct Foo*)0; $x->" + keyword + "; }", "Program\n BEGIN\n =\n variable: $x\n (struct Foo *)\n int: " "0\n " " .\n dereference\n variable: $x\n " + keyword + "\n"); test("BEGIN { $x = (struct Foo)0; $x." + keyword + "; }", "Program\n BEGIN\n =\n variable: $x\n (struct Foo)\n int: 0\n " " .\n variable: $x\n " + keyword + "\n"); test("BEGIN { $x = offsetof(*curtask, " + keyword + "); }", "Program\n BEGIN\n =\n variable: $x\n offsetof: \n " "dereference\n builtin: curtask\n " + keyword + "\n"); } } TEST(Parser, subprog_probe_mixed) { test("i:s:1 {} fn f1(): void {} i:s:1 {} fn f2(): void {}", "Program\n" " f1: void()\n" " f2: void()\n" " interval:s:1\n" " interval:s:1\n"); } TEST(Parser, subprog_void_no_args) { test("fn f(): void {}", "Program\n" " f: void()\n"); } TEST(Parser, subprog_invalid_return_type) { // Error location is incorrect: #3063 test_parse_failure("fn f(): nonexistent {}", R"( stdin:1:9-21: ERROR: syntax error, unexpected identifier, expecting struct or integer type or builtin type or sized type fn f(): nonexistent {} ~~~~~~~~~~~~ )"); } TEST(Parser, subprog_one_arg) { test("fn f($a : uint8): void {}", "Program\n" " f: void($a : uint8)\n"); } TEST(Parser, subprog_two_args) { test("fn f($a : uint8, $b : uint8): void {}", "Program\n" " f: void($a : uint8, $b : uint8)\n"); } TEST(Parser, subprog_string_arg) { test("fn f($a : string[16]): void {}", "Program\n" " f: void($a : string[16])\n"); } TEST(Parser, subprog_struct_arg) { test("fn f($a: struct x): void {}", "Program\n" " f: void($a : struct x)\n"); } TEST(Parser, subprog_union_arg) { test("fn f($a : union x): void {}", "Program\n" " f: void($a : union x)\n"); } TEST(Parser, subprog_enum_arg) { test("fn f($a : enum x): void {}", "Program\n" " f: void($a : enum x)\n"); } TEST(Parser, subprog_invalid_arg) { // Error location is incorrect: #3063 test_parse_failure("fn f($x : invalid): void {}", R"( stdin:1:11-19: ERROR: syntax error, unexpected identifier, expecting struct or integer type or builtin type or sized type fn f($x : invalid): void {} ~~~~~~~~ )"); } TEST(Parser, subprog_return) { test("fn f(): void { return 1 + 1; }", "Program\n" " f: void()\n" " return\n" " +\n" " int: 1\n" " int: 1\n"); } TEST(Parser, subprog_string) { test("fn f(): string[16] {}", "Program\n" " f: string[16]()\n"); } TEST(Parser, subprog_struct) { test("fn f(): struct x {}", "Program\n" " f: struct x()\n"); } TEST(Parser, subprog_union) { test("fn f(): union x {}", "Program\n" " f: union x()\n"); } TEST(Parser, subprog_enum) { test("fn f(): enum x {}", "Program\n" " f: enum x()\n"); } TEST(Parser, for_loop) { test("BEGIN { for ($kv : @map) { print($kv) } }", R"( Program BEGIN for decl variable: $kv expr map: @map stmts call: print variable: $kv )"); // Error location is incorrect: #3063 // No body test_parse_failure("BEGIN { for ($kv : @map) print($kv); }", R"( stdin:1:27-32: ERROR: syntax error, unexpected call, expecting { BEGIN { for ($kv : @map) print($kv); } ~~~~~ )"); // Map for decl test_parse_failure("BEGIN { for (@kv : @map) { } }", R"( stdin:1:13-17: ERROR: syntax error, unexpected map, expecting variable BEGIN { for (@kv : @map) { } } ~~~~ )"); } TEST(Parser, variable_declarations) { test("BEGIN { let $x; }", R"( Program BEGIN decl variable: $x )"); test("BEGIN { let $x: int8; }", R"( Program BEGIN decl variable: $x :: [int8] )"); test("BEGIN { let $x = 1; }", R"( Program BEGIN decl variable: $x int: 1 )"); test("BEGIN { let $x: int8 = 1; }", R"( Program BEGIN decl variable: $x :: [int8] int: 1 )"); // Needs the let keyword test_parse_failure("BEGIN { $x: int8; }", R"( stdin:1:9-12: ERROR: syntax error, unexpected :, expecting ; or } BEGIN { $x: int8; } ~~~ )"); // Needs the let keyword test_parse_failure("BEGIN { $x: int8 = 1; }", R"( stdin:1:9-12: ERROR: syntax error, unexpected :, expecting ; or } BEGIN { $x: int8 = 1; } ~~~ )"); } } // namespace bpftrace::test::parser bpftrace-0.23.2/tests/portability_analyser.cpp000066400000000000000000000057451477746507000215260ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include "ast/passes/field_analyser.h" #include "ast/passes/portability_analyser.h" #include "ast/passes/semantic_analyser.h" #include "clang_parser.h" #include "driver.h" #include "mocks.h" namespace bpftrace::test::portability_analyser { #include "btf_common.h" using ::testing::_; void test(BPFtrace &bpftrace, const std::string &input, int expected_result = 0) { Driver driver(bpftrace); std::stringstream out; std::stringstream msg; msg << "\nInput:\n" << input << "\n\nOutput:\n"; ASSERT_EQ(driver.parse_str(input), 0); ast::FieldAnalyser fields(driver.ctx, bpftrace, out); ASSERT_EQ(fields.analyse(), 0) << msg.str() << out.str(); ClangParser clang; ASSERT_TRUE(clang.parse(driver.ctx.root, bpftrace)); ASSERT_EQ(driver.parse_str(input), 0); out.str(""); ast::SemanticAnalyser semantics(driver.ctx, bpftrace, out, false); ASSERT_EQ(semantics.analyse(), 0) << msg.str() << out.str(); ast::PortabilityAnalyser portability(driver.ctx, out); EXPECT_EQ(portability.analyse(), expected_result) << msg.str() << out.str(); } void test(const std::string &input, int expected_result = 0) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, input, expected_result); } TEST(portability_analyser, generic_field_access_disabled) { test("struct Foo { int x;} BEGIN { $f = (struct Foo *)0; $f->x; }", 1); } TEST(portability_analyser, tracepoint_field_access) { test("tracepoint:sched:sched_one { args }", 0); test("tracepoint:sched:sched_one { args.common_field }", 0); test("tracepoint:sched:sched_* { args.common_field }", 0); // Backwards compatibility test("tracepoint:sched:sched_one { args->common_field }", 0); } class portability_analyser_btf : public test_btf {}; TEST_F(portability_analyser_btf, fentry_field_access) { test("fentry:func_1 { $x = args.a; $y = args.foo1; $z = args.foo2->f.a; }", 0); test("fentry:func_2 { args.foo1 }", 0); test("fentry:func_2, fentry:func_3 { $x = args.foo1; }", 0); // Backwards compatibility test("fentry:func_2 { args->foo1 }", 0); } TEST(portability_analyser, positional_params_disabled) { auto bpftrace = get_mock_bpftrace(); bpftrace->add_param("123"); bpftrace->add_param("hello"); test(*bpftrace, "BEGIN { $1 }", 1); test(*bpftrace, "BEGIN { str($2) }", 1); } TEST(portability_analyser, curtask_disabled) { test("BEGIN { curtask }", 1); test("struct task_struct { char comm[16]; } BEGIN { curtask->comm }", 1); } TEST(portability_analyser, selective_probes_disabled) { test("usdt:/bin/sh:probe { 1 }", 1); test("usdt:/bin/sh:namespace:probe { 1 }", 1); auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "watchpoint:0x10000000:8:rw { 1 }", 1); bpftrace->procmon_ = std::make_unique(123); test(*bpftrace, "watchpoint:0x10000000:8:rw { 1 }", 1); test(*bpftrace, "watchpoint:increment+arg1:4:w { 1 }", 1); test(*bpftrace, "asyncwatchpoint:increment+arg1:4:w { 1 }", 1); } } // namespace bpftrace::test::portability_analyser bpftrace-0.23.2/tests/probe.cpp000066400000000000000000000051161477746507000163650ustar00rootroot00000000000000 #include "ast/passes/codegen_llvm.h" #include "ast/passes/field_analyser.h" #include "ast/passes/resource_analyser.h" #include "ast/passes/semantic_analyser.h" #include "bpftrace.h" #include "clang_parser.h" #include "driver.h" #include "mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace bpftrace::test::probe { #include "btf_common.h" using bpftrace::ast::AttachPoint; using bpftrace::ast::AttachPointList; using bpftrace::ast::Probe; void gen_bytecode(const std::string &input, std::stringstream &out) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); ASSERT_EQ(driver.parse_str(input), 0); ast::FieldAnalyser fields(driver.ctx, *bpftrace); EXPECT_EQ(fields.analyse(), 0); ClangParser clang; clang.parse(driver.ctx.root, *bpftrace); ast::SemanticAnalyser semantics(driver.ctx, *bpftrace); ASSERT_EQ(semantics.analyse(), 0); ast::ResourceAnalyser resource_analyser(driver.ctx, *bpftrace); auto resources_optional = resource_analyser.analyse(); ASSERT_TRUE(resources_optional.has_value()); // clang-tidy doesn't recognize ASSERT_*() as execution terminating // NOLINTNEXTLINE(bugprone-unchecked-optional-access) bpftrace->resources = resources_optional.value(); ast::CodegenLLVM codegen(driver.ctx, *bpftrace); codegen.generate_ir(); codegen.DumpIR(out); } void compare_bytecode(const std::string &input1, const std::string &input2) { std::stringstream expected_output1; std::stringstream expected_output2; gen_bytecode(input1, expected_output1); gen_bytecode(input2, expected_output2); EXPECT_EQ(expected_output1.str(), expected_output2.str()); } TEST(probe, short_name) { compare_bytecode("tracepoint:a:b { args }", "t:a:b { args }"); compare_bytecode("kprobe:f { pid }", "k:f { pid }"); compare_bytecode("kretprobe:f { pid }", "kr:f { pid }"); compare_bytecode("uprobe:sh:f { 1 }", "u:sh:f { 1 }"); compare_bytecode("profile:hz:997 { 1 }", "p:hz:997 { 1 }"); compare_bytecode("hardware:cache-references:1000000 { 1 }", "h:cache-references:1000000 { 1 }"); compare_bytecode("software:faults:1000 { 1 }", "s:faults:1000 { 1 }"); compare_bytecode("interval:s:1 { 1 }", "i:s:1 { 1 }"); } class probe_btf : public test_btf {}; TEST_F(probe_btf, short_name) { compare_bytecode("fentry:func_1 { 1 }", "f:func_1 { 1 }"); compare_bytecode("fexit:func_1 { 1 }", "fr:func_1 { 1 }"); compare_bytecode("iter:task { 1 }", "it:task { 1 }"); compare_bytecode("iter:task_file { 1 }", "it:task_file { 1 }"); compare_bytecode("iter:task_vma { 1 }", "it:task_vma { 1 }"); } } // namespace bpftrace::test::probe bpftrace-0.23.2/tests/procmon.cpp000066400000000000000000000017771477746507000167440ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include #include "child.h" #include "procmon.h" #include "childhelper.h" #include "utils.h" namespace bpftrace::test::procmon { using ::testing::HasSubstr; TEST(procmon, no_such_proc) { try { // NOLINTNEXTLINE(bugprone-unused-raii) ProcMon(1 << 21); FAIL(); } catch (const std::runtime_error &e) { EXPECT_THAT(e.what(), HasSubstr("No such process")); } } TEST(procmon, child_terminates) { std::error_code ec; auto self = std::filesystem::read_symlink("/proc/self/exe", ec); ASSERT_FALSE(ec); auto parent_dir = self.parent_path(); auto out = parent_dir / std::filesystem::path("testprogs/true"); auto child = getChild(out.c_str()); auto procmon = std::make_unique(child->pid()); EXPECT_TRUE(procmon->is_alive()); child->run(); wait_for(child.get(), 1000); EXPECT_FALSE(child->is_alive()); EXPECT_FALSE(procmon->is_alive()); EXPECT_FALSE(procmon->is_alive()); } } // namespace bpftrace::test::procmon bpftrace-0.23.2/tests/required_resources.cpp000066400000000000000000000113171477746507000211700ustar00rootroot00000000000000#include "required_resources.h" #include #include #include #include "format_string.h" #include "struct.h" #include "types.h" namespace bpftrace::test { // ======================================================================== // It's a bit overkill to completely test every field in `RequiredResources` // so for these tests we opt to get coverage on every type we serialize. // ======================================================================== TEST(required_resources, round_trip_simple) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; r.probe_ids.emplace_back("itsastring"); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.probe_ids.size(), 1ul); EXPECT_EQ(r.probe_ids[0], "itsastring"); } } TEST(required_resources, round_trip_field_sized_type) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; r.system_args.emplace_back(FormatString("field0"), std::vector{ Field{ .name = "myfield", .type = CreateInt32(), .offset = 123, .bitfield = Bitfield(1, 2, 0xFF), } }); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.system_args.size(), 1ul); 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); auto &field = fields[0]; EXPECT_EQ(field.name, "myfield"); EXPECT_TRUE(field.type.IsIntTy()); EXPECT_EQ(field.type.GetSize(), 4ul); EXPECT_EQ(field.offset, 123); // clang-tidy does not recognize ASSERT_*() terminates testcase // NOLINTBEGIN(bugprone-unchecked-optional-access) ASSERT_TRUE(field.bitfield.has_value()); EXPECT_EQ(field.bitfield->read_bytes, 1ul); EXPECT_EQ(field.bitfield->access_rshift, 2ul); EXPECT_EQ(field.bitfield->mask, 0xFFul); // NOLINTEND(bugprone-unchecked-optional-access) } } TEST(required_resources, round_trip_map_info) { std::ostringstream serialized(std::ios::binary); { MapInfo info{ .key_type = CreateNone(), .value_type = CreateInet(3), .lhist_args = LinearHistogramArgs{ .min = 99, .max = 123, .step = 33, }, .hist_bits_arg = 1, }; info.key_type = CreateInt32(); RequiredResources r; r.maps_info.insert({ "mymap", info }); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.maps_info.count("mymap"), 1ul); const auto &map_info = r.maps_info["mymap"]; EXPECT_TRUE(map_info.value_type.IsInetTy()); EXPECT_EQ(map_info.value_type.GetSize(), 3ul); EXPECT_TRUE(map_info.key_type.IsIntegerTy()); EXPECT_EQ(map_info.key_type.GetSize(), 4); // clang-tidy does not recognize ASSERT_*() terminates testcase // NOLINTBEGIN(bugprone-unchecked-optional-access) ASSERT_TRUE(map_info.lhist_args.has_value()); EXPECT_EQ(map_info.lhist_args->min, 99); EXPECT_EQ(map_info.lhist_args->max, 123); EXPECT_EQ(map_info.lhist_args->step, 33); // NOLINTEND(bugprone-unchecked-optional-access) EXPECT_TRUE(map_info.hist_bits_arg.has_value()); EXPECT_EQ(map_info.hist_bits_arg, 1); } } TEST(required_resources, round_trip_probes) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; Probe p; p.type = ProbeType::hardware; p.path = "mypath"; p.index = 3; r.special_probes["test"] = std::move(p); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.special_probes.size(), 1ul); auto &probe = r.special_probes["test"]; EXPECT_EQ(probe.type, ProbeType::hardware); EXPECT_EQ(probe.path, "mypath"); EXPECT_EQ(probe.index, 3); } } TEST(required_resources, round_trip_multiple_members) { std::ostringstream serialized(std::ios::binary); { RequiredResources r; r.join_args.emplace_back("joinarg0"); r.time_args.emplace_back("timearg0"); r.save_state(serialized); } std::istringstream input(serialized.str()); { RequiredResources r; r.load_state(input); ASSERT_EQ(r.join_args.size(), 1ul); EXPECT_EQ(r.join_args[0], "joinarg0"); ASSERT_EQ(r.time_args.size(), 1ul); EXPECT_EQ(r.time_args[0], "timearg0"); } } } // namespace bpftrace::test bpftrace-0.23.2/tests/resource_analyser.cpp000066400000000000000000000072671477746507000210140ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include "ast/passes/field_analyser.h" #include "ast/passes/resource_analyser.h" #include "ast/passes/semantic_analyser.h" #include "clang_parser.h" #include "driver.h" #include "mocks.h" namespace bpftrace::test::resource_analyser { using ::testing::_; void test(BPFtrace &bpftrace, const std::string &input, bool expected_result = true, RequiredResources *out_p = nullptr) { Driver driver(bpftrace); std::stringstream out; std::stringstream msg; msg << "\nInput:\n" << input << "\n\nOutput:\n"; ASSERT_EQ(driver.parse_str(input), 0); ast::FieldAnalyser fields(driver.ctx, bpftrace, out); ASSERT_EQ(fields.analyse(), 0) << msg.str() << out.str(); ClangParser clang; ASSERT_TRUE(clang.parse(driver.ctx.root, bpftrace)); ASSERT_EQ(driver.parse_str(input), 0); out.str(""); ast::SemanticAnalyser semantics(driver.ctx, bpftrace, out, false); ASSERT_EQ(semantics.analyse(), 0) << msg.str() << out.str(); ast::ResourceAnalyser resource_analyser(driver.ctx, bpftrace, out); auto resources_optional = resource_analyser.analyse(); EXPECT_EQ(resources_optional.has_value(), expected_result) << msg.str() << out.str(); if (out_p && resources_optional) *out_p = *resources_optional; } void test(const std::string &input, bool expected_result = true, RequiredResources *out = nullptr, std::optional on_stack_limit = std::nullopt) { auto bpftrace = get_mock_bpftrace(); auto configs = ConfigSetter(bpftrace->config_, ConfigSource::script); configs.set(ConfigKeyInt::on_stack_limit, on_stack_limit.value_or(0)); return test(*bpftrace, input, expected_result, out); } TEST(resource_analyser, multiple_hist_bits_in_single_map) { test("BEGIN { @ = hist(1, 1); @ = hist(1, 2); exit()}", false); } TEST(resource_analyser, multiple_lhist_bounds_in_single_map) { test("BEGIN { @[0] = lhist(0, 0, 100000, 1000); @[1] = lhist(0, 0, 100000, " "100); exit() }", false); } TEST(resource_analyser, printf_in_subprog) { test(R"(fn greet(): void { printf("Hello, world\n"); })", true); } TEST(resource_analyser, fmt_string_args_size_ints) { RequiredResources resources; test(R"(BEGIN { printf("%d %d", 3, 4) })", true, &resources); EXPECT_EQ(resources.max_fmtstring_args_size, 24); } TEST(resource_analyser, fmt_string_args_below_on_stack_limit) { RequiredResources resources; test(R"(BEGIN { printf("%d %d", 3, 4) })", true, &resources, 32); EXPECT_EQ(resources.max_fmtstring_args_size, 0); } TEST(resource_analyser, fmt_string_args_size_arrays) { RequiredResources resources; test( R"(struct Foo { int a; char b[10]; } BEGIN { $foo = (struct Foo *)0; $foo2 = (struct Foo *)1; printf("%d %s %d %s\n", $foo->a, $foo->b, $foo2->a, $foo2->b) })", true, &resources); EXPECT_EQ(resources.max_fmtstring_args_size, 56); } TEST(resource_analyser, fmt_string_args_size_strings) { RequiredResources resources; test( R"(BEGIN { printf("%dst: %sa; %dnd: %sb;; %drd: %sc;;; %dth: %sd;;;;\n", 1, "a", 2, "ab", 3, "abc", 4, "abcd") })", true, &resources); EXPECT_EQ(resources.max_fmtstring_args_size, 72); } TEST(resource_analyser, fmt_string_args_non_map_print_int) { RequiredResources resources; test(R"(BEGIN { print(5) })", true, &resources); EXPECT_EQ(resources.max_fmtstring_args_size, 24); } TEST(resource_analyser, fmt_string_args_non_map_print_arr) { RequiredResources resources; test( R"(struct Foo { char a[24]; } BEGIN { print(5); $foo = (struct Foo *)0; print($foo->a) })", true, &resources); EXPECT_EQ(resources.max_fmtstring_args_size, 40); } } // namespace bpftrace::test::resource_analyser bpftrace-0.23.2/tests/return_path_analyser.cpp000066400000000000000000000051711477746507000215100ustar00rootroot00000000000000#include "gmock/gmock.h" #include "gtest/gtest.h" #include "ast/passes/field_analyser.h" #include "ast/passes/return_path_analyser.h" #include "ast/passes/semantic_analyser.h" #include "clang_parser.h" #include "driver.h" #include "mocks.h" namespace bpftrace::test::return_path_analyser { using ::testing::_; void test(BPFtrace &bpftrace, const std::string &input, int expected_result = 0) { Driver driver(bpftrace); std::stringstream out; std::stringstream msg; msg << "\nInput:\n" << input << "\n\nOutput:\n"; ASSERT_EQ(driver.parse_str(input), 0); ast::FieldAnalyser fields(driver.ctx, bpftrace, out); ASSERT_EQ(fields.analyse(), 0) << msg.str() << out.str(); ClangParser clang; ASSERT_TRUE(clang.parse(driver.ctx.root, bpftrace)); ASSERT_EQ(driver.parse_str(input), 0); out.str(""); ast::SemanticAnalyser semantics(driver.ctx, bpftrace, out, false); ASSERT_EQ(semantics.analyse(), 0) << msg.str() << out.str(); ast::ReturnPathAnalyser return_path(driver.ctx, out); EXPECT_EQ(return_path.analyse(), expected_result) << msg.str() << out.str(); } void test(const std::string &input, int expected_result = 0) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, input, expected_result); } TEST(return_path_analyser, simple_return) { test("fn test(): int64 { $x = 0; return 0; }", 0); } TEST(return_path_analyser, simple_no_return) { test("fn test(): int64 { $x = 0; }", 1); } TEST(return_path_analyser, if_else) { test("fn test($x: int64): int64 {" " if ($x > 0) { return 0; } else { return 1; }" "}", 0); } TEST(return_path_analyser, if_else_no_return) { test("fn test($x: int64): int64 {" " if ($x > 0) { return 0; } else { $x = 0; }" "}", 1); } TEST(return_path_analyser, if_without_else) { test("fn test($x: int64): int64 { if ($x > 0) { return 0; } }", 1); } TEST(return_path_analyser, while_loop) { test("fn test($x: int64): int64 { while ($x) { return 0; } }", 1); } TEST(return_path_analyser, if_branches) { test("fn test($x: int64): int64 {" " if ($x > 0) {" " if ($x > 0) { return 1; } else { return 0; }" " } else {" " if ($x > 0) { return 1; } else { return 0; }" " }" "}", 0); } TEST(return_path_analyser, if_branches_fail) { test("fn test($x: int64): int64 {" " if ($x > 0) {" " if ($x > 0) { return 1; } else { return 0; }" " } else {" " if ($x > 0) { return 1; } else { $x = 1; }" " }" "}", 1); } TEST(return_path_analyser, void_return_type) { test("fn test() : void {}", 0); } } // namespace bpftrace::test::return_path_analyser bpftrace-0.23.2/tests/runtime-tests.sh000077500000000000000000000007311477746507000177320ustar00rootroot00000000000000#!/usr/bin/env bash if [[ $EUID -ne 0 ]]; then >&2 echo "Must be run as root" exit 1 fi set -e; pushd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 BPFTRACE_RUNTIME_TEST_EXECUTABLE=${BPFTRACE_RUNTIME_TEST_EXECUTABLE:-@CMAKE_BINARY_DIR@/src/bpftrace}; export BPFTRACE_RUNTIME_TEST_EXECUTABLE; echo "====================" echo "bpftrace --info:" echo "====================" "${BPFTRACE_RUNTIME_TEST_EXECUTABLE}" --info python3 -u runtime/engine/main.py "$@" bpftrace-0.23.2/tests/runtime/000077500000000000000000000000001477746507000162325ustar00rootroot00000000000000bpftrace-0.23.2/tests/runtime/addrspace000066400000000000000000000003741477746507000201070ustar00rootroot00000000000000NAME openat uptr RUN {{BPFTRACE}} -e 't:syscalls:sys_enter_openat /comm == "syscall"/ { print(str(uptr(args.filename))) }' -c "./testprogs/syscall openat" EXPECT_REGEX ^.*/bpftrace_runtime_test_syscall_gen_open_temp$ REQUIRES_FEATURE probe_read_kernel bpftrace-0.23.2/tests/runtime/array000066400000000000000000000164161477746507000173030ustar00rootroot00000000000000NAME array element access - assign to map PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = (struct A *) arg0; @x = $a->x[0]; exit(); } EXPECT @x: 1 AFTER ./testprogs/array_access NAME array element access - assign to var PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = (struct A *) arg0; $x = $a->x[0]; printf("Result: %d\n", $x); exit(); } EXPECT Result: 1 AFTER ./testprogs/array_access NAME array element access - out of bounds PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = (struct A *) arg0; $x = $a->x[5]; printf("%d\n", $x); exit(); } EXPECT stdin:1:100-108: ERROR: the index 5 is out of bounds for array of size 4 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(); } EXPECT Result: 1 AFTER ./testprogs/array_access NAME array element access via assignment into map PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @a[0] = ((struct A *) arg0)->x; printf("Result: %d\n", @a[0][0]); exit(); } EXPECT Result: 1 AFTER ./testprogs/array_access NAME array element access via assignment into map and var PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @a[0] = ((struct A *) arg0)->x; $x = @a[0]; printf("Result: %d\n", $x[0]); exit(); } EXPECT Result: 1 AFTER ./testprogs/array_access NAME array assignment into map PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @a = ((struct A *) arg0)->x; exit(); } EXPECT @a: [1,2,3,4] AFTER ./testprogs/array_access NAME array as a map key PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @x[((struct A *) arg0)->x] = 0; exit(); } EXPECT @x[[1,2,3,4]]: 0 AFTER ./testprogs/array_access NAME array as a part of map multikey PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @x[((struct A *) arg0)->x, 42] = 0; exit(); } EXPECT @x[[1,2,3,4], 42]: 0 AFTER ./testprogs/array_access NAME array as a map key assigned from another map PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @a = ((struct A *) arg0)->x; @x[@a] = 0; exit(); } EXPECT @x[[1,2,3,4]]: 0 AFTER ./testprogs/array_access NAME array in a tuple PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { @x = (1, ((struct A *) arg0)->x); exit(); } EXPECT @x: (1, [1,2,3,4]) AFTER ./testprogs/array_access NAME multi-dimensional array element access PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { $x = ((struct B *) arg1)->y[1][0]; printf("Result: %d\n", $x); exit(); } EXPECT Result: 7 AFTER ./testprogs/array_access NAME multi-dimensional array element access via assignment into var PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { $b = ((struct B *) arg1)->y; $y = $b[1][0]; printf("Result: %d\n", $y); exit(); } EXPECT Result: 7 AFTER ./testprogs/array_access NAME multi-dimensional array element access via assignment into map PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @b[0] = ((struct B *) arg1)->y; printf("Result: %d\n", @b[0][1][0]); exit(); } EXPECT Result: 7 AFTER ./testprogs/array_access NAME multi-dimensional array element access via assignment into map and var PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @b[0] = ((struct B *) arg1)->y; $x = @b[0]; printf("Result: %d\n", $x[1][0]); exit(); } EXPECT Result: 7 AFTER ./testprogs/array_access NAME multi-dimensional array assignment into map PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @b = ((struct B *) arg1)->y; exit(); } EXPECT @b: [[5,6],[7,8]] AFTER ./testprogs/array_access NAME multi-dimensional array as a map key PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @x[((struct B *) arg1)->y] = 0; exit(); } EXPECT @x[[[5,6],[7,8]]]: 0 AFTER ./testprogs/array_access NAME multi-dimensional array as a map key assigned from another map PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { @b = ((struct B *) arg1)->y; @x[@b] = 0; exit(); } EXPECT @x[[[5,6],[7,8]]]: 0 AFTER ./testprogs/array_access NAME array as pointer element access - assign to map PROG uprobe:./testprogs/array_access:test_array { $a = (int32 *) arg0; @x = $a[0]; exit(); } EXPECT @x: 1 AFTER ./testprogs/array_access NAME array as pointer element access - assign to var PROG uprobe:./testprogs/array_access:test_array { $a = (int32 *) arg0; $x = $a[0]; printf("Result: %d\n", $x); exit(); } EXPECT Result: 1 AFTER ./testprogs/array_access NAME array as pointer access via assignment into var PROG uprobe:./testprogs/array_access:test_array { $a = (int32 *) arg0; $x = $a[0]; printf("Result: %d\n", $x); exit(); } EXPECT Result: 1 AFTER ./testprogs/array_access NAME array as pointer element access via assignment into map PROG uprobe:./testprogs/array_access:test_array { @a[0] = (int32 *) arg0; printf("Result: %d\n", @a[0][0]); exit(); } EXPECT Result: 1 AFTER ./testprogs/array_access NAME array as pointer element access via assignment into map and var PROG uprobe:./testprogs/array_access:test_array { @a[0] = (int32 *) arg0; $x = @a[0]; printf("Result: %d\n", $x[0]); exit(); } EXPECT Result: 1 AFTER ./testprogs/array_access NAME array element access via positional index RUN {{BPFTRACE}} -e 'struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = (struct A *) arg0; $x = $a->x[$1]; printf("Result: %d\n", $x); exit(); }' 0 EXPECT Result: 1 AFTER ./testprogs/array_access NAME array of pointers element access PROG struct C { int *z[4]; } uprobe:./testprogs/array_access:test_ptr_array { @x = *((struct C*)arg0)->z[1]; exit(); } EXPECT @x: 2 AFTER ./testprogs/array_access NAME array compare eq PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_arrays { $a = (struct A *) arg0; $b = (struct A *) arg0; if ($a->x == $b->x) { printf("two int arrays are equal.\n"); } exit(); } EXPECT two int arrays are equal. AFTER ./testprogs/array_access NAME array compare ne PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_arrays { $a = (struct A *) arg0; $b = (struct A *) arg1; if ($a->x != $b->x) { printf("two int arrays are not equal.\n"); } exit(); } EXPECT two int arrays are not equal. AFTER ./testprogs/array_access NAME variable array element access - assign to map PROG struct D { int x; int y[0]; } uprobe:./testprogs/array_access:test_variable_array { $a = (struct D *) arg0; @y = $a->y[0]; exit(); } EXPECT @y: 1 AFTER ./testprogs/array_access NAME cast literal to int array PROG BEGIN { printf("first byte: %x\n", ((int8[8])12345)[0]); exit(); } EXPECT first byte: 39 TIMEOUT 1 NAME cast to int array with automatic size PROG BEGIN { printf("byte: %x\n", ((int8[])12345)[0]); exit(); } EXPECT byte: 39 TIMEOUT 1 NAME cast int array to int internal PROG BEGIN { printf("ip: %x\n", (uint32)pton("127.0.0.1")); exit(); } EXPECT ip: 100007f AFTER ./testprogs/array_access NAME cast int array to int proberead RUN {{BPFTRACE}} --include "stdint.h" -e 'struct A { int x[4]; uint8_t y[4]; } uprobe:./testprogs/array_access:test_arrays { $y = (int32)((struct A *)arg0)->y; printf("y: %x\n", $y); exit()}' EXPECT y: ddccbbaa AFTER ./testprogs/array_access bpftrace-0.23.2/tests/runtime/basic000066400000000000000000000212271477746507000172420ustar00rootroot00000000000000NAME it shows version RUN {{BPFTRACE}} --version EXPECT_REGEX ^bpftrace v\d TIMEOUT 1 NAME it shows usage with help flag RUN {{BPFTRACE}} -h EXPECT USAGE: TIMEOUT 1 NAME it shows usage with bad flag 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 EXPECT hello world! TIMEOUT 1 NAME it lists kprobes RUN {{BPFTRACE}} -l | grep kprobes EXPECT_REGEX kprobe:.* TIMEOUT 1 NAME it lists tracepoints RUN {{BPFTRACE}} -l | grep tracepoint EXPECT_REGEX tracepoint:.* TIMEOUT 1 NAME it lists software events RUN {{BPFTRACE}} -l | grep software EXPECT_REGEX software:.* TIMEOUT 1 NAME it lists hardware events RUN {{BPFTRACE}} -l | grep hardware EXPECT_REGEX hardware:.* TIMEOUT 1 NAME it lists fentry RUN {{BPFTRACE}} -l | grep fentry EXPECT_REGEX fentry:.* REQUIRES_FEATURE btf REQUIRES_FEATURE fentry TIMEOUT 1 NAME it lists rawtracepoints RUN {{BPFTRACE}} -l | grep rawtracepoint EXPECT_REGEX rawtracepoint:.* TIMEOUT 1 NAME it lists fentry params RUN {{BPFTRACE}} -lv "fentry:*" EXPECT_REGEX [ ]+[a-zA-Z_\*\s]+ REQUIRES_FEATURE btf REQUIRES_FEATURE fentry TIMEOUT 1 NAME it lists kprobes with regex filter RUN {{BPFTRACE}} -l "kprobe:*" EXPECT_REGEX kprobe:.* TIMEOUT 1 NAME it lists kretprobes with regex filter RUN {{BPFTRACE}} -l "kretprobe:*" EXPECT_REGEX kretprobe:.* TIMEOUT 1 NAME it lists uprobes with regex filter RUN {{BPFTRACE}} -l "uprobe:./testprogs/syscall:*" EXPECT_REGEX uprobe:.* TIMEOUT 1 NAME it lists uretprobes with regex filter RUN {{BPFTRACE}} -l "uretprobe:./testprogs/syscall:*" EXPECT_REGEX uretprobe:.* TIMEOUT 1 NAME it lists tracepoints with regex filter RUN {{BPFTRACE}} -l "tracepoint:raw_syscalls:*" EXPECT tracepoint:raw_syscalls:sys_exit TIMEOUT 1 NAME it lists software events with regex filter RUN {{BPFTRACE}} -l "software:*" EXPECT software:cpu: TIMEOUT 1 NAME it lists hardware events with regex filter RUN {{BPFTRACE}} -l "hardware:*" EXPECT hardware:cpu-cycles: TIMEOUT 1 NAME it lists fentry events with regex filter RUN {{BPFTRACE}} -l "fentry:*" EXPECT_REGEX fentry:.* REQUIRES_FEATURE btf REQUIRES_FEATURE fentry TIMEOUT 1 NAME it lists fexit events with regex filter RUN {{BPFTRACE}} -l "fexit:*" EXPECT_REGEX fexit:.* REQUIRES_FEATURE btf REQUIRES_FEATURE fentry TIMEOUT 1 NAME it lists interval probes with regex filter RUN {{BPFTRACE}} -l "interval:*" EXPECT interval:hz: EXPECT interval:us: EXPECT interval:ms: EXPECT interval:s: TIMEOUT 1 NAME it lists profile probes with regex filter RUN {{BPFTRACE}} -l "profile:*" EXPECT profile:hz: EXPECT profile:us: EXPECT profile:ms: EXPECT profile:s: TIMEOUT 1 NAME listing with wildcarded probe type RUN {{BPFTRACE}} -l "*ware:*" EXPECT_REGEX hardware:.* EXPECT_REGEX software:.* TIMEOUT 1 NAME it lists rawtracepoint with regex filter RUN {{BPFTRACE}} -l "rawtracepoint:*" EXPECT_REGEX rawtracepoint:.* TIMEOUT 1 NAME it only lists probes in the program RUN {{BPFTRACE}} -l -e 'fentry:vmlinux:vfs_read { exit(); }' EXPECT fentry:vmlinux:vfs_read EXPECT_NONE fentry:vmlinux:vfs_write NAME it lists uprobes in the program RUN {{BPFTRACE}} -l -e 'uretprobe:*:uprobeFunction* { exit(); }' -p {{BEFORE_PID}} EXPECT_REGEX uretprobe:[\S]+uprobe_test:uprobeFunction1 BEFORE ./testprogs/uprobe_test NAME it lists multiple probes in the program RUN {{BPFTRACE}} -l -e 'hardware:cache-misses:10 { exit(); } tracepoint:xdp:mem_connect { exit(); }' EXPECT hardware:cache-misses: EXPECT tracepoint:xdp:mem_connect NAME it lists struct definitions RUN {{BPFTRACE}} -lv 'struct task_struct' EXPECT struct task_struct { TIMEOUT 2 NAME it lists probes in a given file RUN {{BPFTRACE}} -l runtime/scripts/interval_order.bt EXPECT interval:ms: EXPECT interval:s: EXPECT interval:us: NAME warning on non existent file RUN {{BPFTRACE}} -l non_existent_file.bt EXPECT WARNING: It appears that 'non_existent_file.bt' is a filename but the file does not exist. Treating 'non_existent_file.bt' as a search pattern. TIMEOUT 1 WILL_FAIL NAME pid fails validation with leading non-number RUN {{BPFTRACE}} -p a1111 file.bt EXPECT ERROR: Failed to parse pid: 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 file.bt EXPECT ERROR: Failed to parse pid: 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 file.bt EXPECT ERROR: Failed to parse pid: 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 file.bt EXPECT ERROR: Failed to parse pid: 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 EXPECT_NONE 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 definitions.h:2:10: fatal error: 'lol/no.h' file not found TIMEOUT 1 WILL_FAIL NAME defines work RUN {{BPFTRACE}} -e "$(echo '#define _UNDERSCORE 314'; echo 'BEGIN { printf("%d\n", _UNDERSCORE); exit(); }')" EXPECT 314 TIMEOUT 1 NAME clear map PROG BEGIN { @ = 1; @a[1] = 1; clear(@); clear(@a); printf("ok\n"); exit(); } EXPECT ok TIMEOUT 1 NAME clear count-map PROG BEGIN { @ = count(); @a[1] = count(); clear(@); clear(@a); exit(); } EXPECT @: 0 TIMEOUT 1 NAME delete map PROG BEGIN { @ = 1; @a[1] = 1; @b[1, 2] = 2; delete(@); delete(@a, 1); delete(@b, (1, 2)); printf("ok\n"); exit(); } EXPECT ok TIMEOUT 1 NAME delete count-map PROG BEGIN { @ = count(); @a[1] = count(); delete(@); delete(@a, 1); exit(); } EXPECT @: 0 TIMEOUT 1 NAME delete deprecated PROG BEGIN { @a[1] = 1; @b[2, "hi"] = 2; delete(@a[1]); delete(@b[2, "hi"]); exit(); } EXPECT_NONE @a[1]: 1 EXPECT_NONE @b[2, hi]: 2 TIMEOUT 1 NAME increment/decrement map PROG BEGIN { @x = 10; printf("%d", @x++); printf(" %d", ++@x); printf(" %d", @x--); printf(" %d\n", --@x); delete(@x); exit(); } EXPECT 10 12 12 10 TIMEOUT 1 NAME parallel map access RUN {{BPFTRACE}} runtime/scripts/parallel_map_access.bt --no-warnings EXPECT SUCCESS TIMEOUT 10 NAME increment/decrement variable PROG BEGIN { $x = 10; printf("%d", $x++); printf(" %d", ++$x); printf(" %d", $x--); printf(" %d\n", --$x); exit(); } EXPECT 10 12 12 10 TIMEOUT 1 NAME spawn child RUN {{BPFTRACE}} -e 'i:ms:500 { printf("%d\n", cpid); }' -c './testprogs/syscall nanosleep 1e9' EXPECT_REGEX [0-9]+ TIMEOUT 3 NAME info flag RUN {{BPFTRACE}} --info EXPECT_REGEX perf_event_array: yes TIMEOUT 1 NAME basic while loop PROG BEGIN { $a = 0; while ($a <= 100) { @=avg($a++) } exit(); } EXPECT @: 50 REQUIRES_FEATURE loop NAME disable warnings RUN {{BPFTRACE}} --no-warnings -e 'BEGIN { @x = stats(10); print(@x, 2); clear(@x); exit();}' 2>&1| grep -c -E "WARNING|invalid option" EXPECT_REGEX ^0$ TIMEOUT 1 WILL_FAIL NAME kaddr fails PROG BEGIN { print(kaddr("asdfzzzzzzz")) } EXPECT ERROR: Failed to compile: Failed to resolve kernel symbol: asdfzzzzzzz TIMEOUT 1 WILL_FAIL NAME variable strings are memset PROG BEGIN { $x = "xxxxx"; $x = "a"; @[$x] = 1; $y = "yyyyy"; $y = "a"; @[$y] = 1; printf("len: %d\n", len(@)); exit(); } EXPECT len: 1 TIMEOUT 1 NAME map strings are memset PROG BEGIN { @x = "xxxxx"; @x = "a"; @[@x] = 1; @y = "yyyyy"; @y = "a"; @[@y] = 1; printf("len: %d\n", len(@)); exit(); } EXPECT len: 1 TIMEOUT 1 NAME print_per_cpu_map_vals PROG BEGIN { @a = avg(5); @c = count(); @s = sum(5); @mn = min(1); @mx = max(-1); print((@a, @c, @s, @mn, @mx)); exit(); } EXPECT (5, 1, 5, 1, -1) TIMEOUT 3 NAME print large int PROG BEGIN { $a = 10223372036854775807; print(($a)); exit(); } EXPECT 10223372036854775807 TIMEOUT 1 NAME has_key exists PROG BEGIN { @a[1] = 0; if (has_key(@a, 1)) { printf("ok\n"); } exit(); } EXPECT ok NAME has_key no_exists PROG BEGIN { @a[1] = 0; if (has_key(@a, 2)) { printf("ok\n"); } exit(); } EXPECT_NONE ok NAME has_key complex tuple PROG BEGIN { @a[1, ("hello", (int8)5)] = 0; if (has_key(@a, (1, ("hello", 5)))) { printf("ok\n"); } exit(); } EXPECT ok NAME has_key map value as key PROG BEGIN { @g = 2; @a[2] = 0; if (has_key(@a, @g)) { printf("ok\n"); } exit(); } EXPECT ok NAME has_key map keys and values PROG BEGIN { @a[1] = 1; @b[has_key(@a, 1)] = has_key(@a, 2); exit(); } EXPECT @b[1]: 0 NAME has_key as a variable PROG BEGIN { @a[1] = 1; $b = has_key(@a, 1); $c = has_key(@a, 2); print(($b, $c)); exit(); } EXPECT (1, 0) NAME exit code PROG BEGIN { exit(69); } RETURN_CODE 69 TIMEOUT 1 bpftrace-0.23.2/tests/runtime/btf000066400000000000000000000061131477746507000167310ustar00rootroot00000000000000NAME user_supplied_c_def_using_btf PROG struct foo { struct task_struct t; } BEGIN { exit(); } EXPECT Attaching 1 probe... REQUIRES_FEATURE btf NAME tracepoint_pointer_type_resolution PROG tracepoint:syscalls:sys_enter_nanosleep { args.rqtp->tv_sec; exit(); } EXPECT Attaching 1 probe... REQUIRES_FEATURE btf NAME tracepoint_nested_pointer_type_resolution PROG tracepoint:napi:napi_poll { args.napi->dev->name; exit(); } EXPECT Attaching 1 probe... REQUIRES_FEATURE btf NAME enum_value_reference PROG BEGIN { printf("%d\n", sizeof(BTF_VAR_STATIC)); exit(); } EXPECT_REGEX ^8$ REQUIRES_FEATURE btf NAME redefine_btf_type PROG struct task_struct { int x; } BEGIN { printf("%d\n", curtask->x); exit() } EXPECT_REGEX -?[0-9][0-9]* REQUIRES_FEATURE btf NAME redefine_btf_type_missing_def PROG struct task_struct { struct thread_info x; } BEGIN { printf("%d\n", curtask->x.status); } EXPECT_REGEX error:.*'struct thread_info' REQUIRES_FEATURE btf WILL_FAIL NAME kernel_module_attach RUN {{BPFTRACE}} -e 'fentry:nf_tables_newtable { printf("hit\n"); exit(); }' AFTER nft add table bpftrace EXPECT hit REQUIRES_FEATURE fentry REQUIRES lsmod | grep '^nf_tables' REQUIRES nft --help CLEANUP nft delete table bpftrace NAME kernel_module_attach_wildcard RUN {{BPFTRACE}} -e 'fentry:nf_table*:nf_tables_newtable { printf("hit\n"); exit(); }' AFTER nft add table bpftrace EXPECT hit REQUIRES_FEATURE fentry REQUIRES lsmod | grep '^nf_tables' REQUIRES nft --help CLEANUP nft delete table bpftrace NAME kernel_module_args RUN {{BPFTRACE}} -e 'fentry:nf_tables_newtable { printf("skb: %p\n", args.skb); exit(); }' AFTER nft add table bpftrace EXPECT_REGEX ^skb: 0x[a-f0-9]+$ REQUIRES_FEATURE fentry REQUIRES lsmod | grep '^nf_tables' REQUIRES nft --help CLEANUP nft delete table bpftrace NAME kernel_module_types RUN {{BPFTRACE}} -e 'fentry:nf_tables_newtable { printf("skb len: %d\n", args.skb->len); exit(); }' AFTER nft add table bpftrace EXPECT_REGEX ^skb len: [0-9]+$ REQUIRES_FEATURE fentry REQUIRES lsmod | grep '^nf_tables' REQUIRES nft --help CLEANUP nft delete table bpftrace NAME kernel_module_tracepoint PROG tracepoint:xfs:xfs_setfilesize { print(args.offset) } i:ms:1 { exit(); } EXPECT Attaching 2 probes... REQUIRES_FEATURE btf REQUIRES lsmod | grep "^xfs" # args.ctxt has type 'struct x86_emulate_ctxt' which is forward-defined in # 'vmlinux' BTF and fully defined in 'kvm' BTF. # This tests checks that the correct BTF definition is pulled from 'kvm'. NAME kernel_module_type_fwd PROG fentry:kvm:x86_emulate_insn { printf("%d\n", args.ctxt->mode); exit(); } EXPECT Attaching 1 probe... TIMEOUT 2 REQUIRES_FEATURE fentry REQUIRES lsmod | grep '^kvm' NAME kprobe_kernel_module_type_fwd PROG kprobe:kvm:x86_emulate_insn { $ctxt = (struct x86_emulate_ctxt *) arg0; printf("%d\n", $ctxt->mode); exit(); } EXPECT Attaching 1 probe... TIMEOUT 2 REQUIRES lsmod | grep '^kvm' # This test only matters on Clang-built kernels, which support btf_type_tag # and require special handling in bpftrace NAME btf_type_tag_access PROG BEGIN { printf("SUCCESS %d\n", curtask->parent->pid); exit() } EXPECT_REGEX SUCCESS [0-9][0-9]* REQUIRES_FEATURE btf bpftrace-0.23.2/tests/runtime/builtin000066400000000000000000000165051477746507000176320ustar00rootroot00000000000000NAME pid PROG i:ms:1 { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* NAME tid PROG i:ms:1 { printf("SUCCESS %d\n", tid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* NAME uid PROG i:ms:1 { printf("SUCCESS %d\n", uid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* NAME gid PROG i:ms:1 { printf("SUCCESS %d\n", gid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* NAME nsecs PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME elapsed PROG i:ms:1 { printf("SUCCESS %llu\n", elapsed); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME numaid PROG i:ms:1 { printf("SUCCESS %lu\n", numaid); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME cpu PROG i:ms:1 { printf("SUCCESS %lu\n", cpu); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME comm PROG BEGIN { printf("SUCCESS %s\n", comm); exit(); } EXPECT SUCCESS bpftrace NAME kstack PROG BEGIN { printf("%s\n", kstack); exit(); } EXPECT Attaching 1 probe... NAME ustack PROG BEGIN { printf("%s\n", ustack); exit(); } EXPECT Attaching 1 probe... NAME arg PROG k:vfs_read { printf("SUCCESS %p\n", arg0); exit(); } EXPECT_REGEX ^SUCCESS 0x[0-9a-f]+$ AFTER ./testprogs/syscall read NAME retval PROG kretprobe:vfs_read { printf("SUCCESS %d\n", retval); exit(); } EXPECT_REGEX SUCCESS .* AFTER ./testprogs/syscall read NAME func_kprobe PROG k:vfs_read { printf("func: '%s'\n", func); exit(); } EXPECT func: 'vfs_read' AFTER ./testprogs/syscall read NAME func_kretprobe PROG kr:vfs_read { printf("func: '%s'\n", func); exit(); } EXPECT func: 'vfs_read' REQUIRES_FEATURE get_func_ip AFTER ./testprogs/syscall read NAME func_uprobe PROG uprobe:./testprogs/uprobe_symres:test { printf("func: '%s'\n", func); exit(); } EXPECT func: 'test' AFTER ./testprogs/uprobe_symres NAME func_uretprobe PROG uretprobe:./testprogs/uprobe_symres:test { printf("func: '%s'\n", func); exit(); } # Kernels from v5.15 to v6.5 include the get_func_ip helper, but it does not # work for uretprobes: it will always return 0. EXPECT_REGEX ^func: 'test'$|^func: '0'$ AFTER ./testprogs/uprobe_symres REQUIRES_FEATURE get_func_ip # Disabled, since BCC code it depends on is prone to race condition, # (https://github.com/iovisor/bcc/pull/4319#issuecomment-1321731687) NAME func_uprobe_symcache_preload ENV BPFTRACE_CACHE_USER_SYMBOLS=PER_PID PROG uprobe:./testprogs/uprobe_symres_exited_process:test { print(func); exit(); } EXPECT test BEFORE ./testprogs/uprobe_symres_exited_process REQUIRES bash -c "exit 1" NAME func_uprobe_elf_symtable ENV BPFTRACE_CACHE_USER_SYMBOLS=PER_PROGRAM PROG uprobe:./testprogs/uprobe_symres_exited_process:test { print(func); exit(); } EXPECT test AFTER ./testprogs/disable_aslr ./testprogs/uprobe_symres_exited_process NAME username PROG i:ms:1 { printf("SUCCESS %s\n", username); exit(); } EXPECT_REGEX SUCCESS .* NAME probe PROG k:do_nanosleep { printf("SUCCESS %s\n", probe); exit(); } EXPECT SUCCESS kprobe:do_nanosleep AFTER ./testprogs/syscall nanosleep 1e8 NAME begin probe PROG BEGIN { printf("%s", probe);exit(); } END{printf("-%s\n", probe); } EXPECT_REGEX ^BEGIN-END$ AFTER ./testprogs/syscall nanosleep 1e8 NAME curtask PROG i:ms:1 { printf("SUCCESS %p\n", curtask); exit(); } EXPECT_REGEX SUCCESS 0x[0-9a-f]+ NAME curtask_field PROG struct task_struct {int x;} i:ms:1 { printf("SUCCESS %d\n", curtask->x); exit(); } EXPECT_REGEX SUCCESS -?[0-9][0-9]* NAME rand PROG i:ms:1 { printf("SUCCESS %lu\n", rand); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME cgroup PROG i:ms:1 { printf("SUCCESS %llu\n", cgroup); exit(); } EXPECT_REGEX SUCCESS [0-9]+ MIN_KERNEL 4.18 NAME ctx PROG struct x {unsigned long x}; i:ms:1 { printf("SUCCESS %lu\n", ((struct x*)ctx)->x); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME cat PROG i:ms:1 { cat("/proc/loadavg"); exit(); } EXPECT_REGEX ^([0-9]+\.[0-9]+ ?)+.*$ NAME cat limited output ENV BPFTRACE_MAX_CAT_BYTES=1 PROG i:ms:1 { cat("/proc/loadavg"); exit(); } EXPECT_REGEX ^[0-9]$ NAME cat format str PROG i:ms:1 { $s = "loadavg"; cat("/proc/%s", $s); exit(); } EXPECT_REGEX ^([0-9]+\.[0-9]+ ?)+.*$ NAME log size too small ENV BPFTRACE_LOG_SIZE=2 RUN {{BPFTRACE}} -v -e 'BEGIN { if (str($1) == str($2)) { printf("%s\n", str($1)); exit() } }' "hello" "hello" EXPECT ERROR: Error loading BPF program for BEGIN_1. EXPECT_REGEX ^WARNING: Kernel log seems to be trimmed.* WILL_FAIL NAME increase log size ENV BPFTRACE_LOG_SIZE=10000000 RUN {{BPFTRACE}} -e 'BEGIN { if (str($1) == str($2)) { printf("%s\n", str($1)); exit() } }' "hello" "hello" EXPECT hello NAME cat "no such file" PROG i:ms:1 { cat("/does/not/exist/file"); exit(); } EXPECT ERROR: failed to open file '/does/not/exist/file': No such file or directory NAME sizeof PROG struct Foo { int x; char c; } BEGIN { $x = 1; printf("%d %d %d %d %d\n", sizeof(struct Foo), sizeof((*(struct Foo*)0).x), sizeof((*(struct Foo*)0).c), sizeof(1 == 1), sizeof($x)); exit(); } EXPECT 8 4 1 8 8 NAME sizeof_ints PROG BEGIN { printf("%d %d %d %d %d %d\n", sizeof(uint8), sizeof(int8), sizeof(uint16), sizeof(int16), sizeof(uint32), sizeof(int32)); exit(); } EXPECT 1 1 2 2 4 4 # printf only takes 7 args NAME sizeof_ints_pt2 PROG BEGIN { printf("%d %d\n", sizeof(uint64), sizeof(int64)); exit(); } EXPECT 8 8 NAME sizeof_btf PROG BEGIN { printf("size=%d\n", sizeof(struct task_struct)); exit(); } EXPECT_REGEX ^size= REQUIRES_FEATURE btf NAME offsetof PROG struct Foo { int x; struct Bar { int x; } bar; } BEGIN { printf("%ld %ld\n", offsetof(struct Foo, x), offsetof(struct Foo, bar.x)); exit(); } EXPECT_REGEX ^0 4$ NAME print args in fentry PROG fentry:vfs_open { print(args); exit(); } EXPECT_REGEX { .path = 0x[0-9a-f]+, .file = 0x[0-9a-f]+ } REQUIRES_FEATURE fentry AFTER ./testprogs/syscall open NAME args in fentry store in map PROG fentry:vfs_open { @= args; exit(); } EXPECT_REGEX @: { .path = 0x[0-9a-f]+, .file = 0x[0-9a-f]+ } REQUIRES_FEATURE fentry AFTER ./testprogs/syscall open NAME args in fentry as a map key PROG fentry:vfs_open { @[args] = 1; exit(); } EXPECT_REGEX @[{ .path = 0x[0-9a-f]+, .file = 0x[0-9a-f]+ }]: 1 REQUIRES_FEATURE fentry AFTER ./testprogs/syscall open NAME args in uprobe print PROG uprobe:./testprogs/uprobe_test:uprobeFunction1 { print(args); exit(); } EXPECT_REGEX { .n = 0x[0-9a-f]+, .c = 120 } REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME args in uprobe store in map PROG uprobe:./testprogs/uprobe_test:uprobeFunction1 { @ = args; exit(); } EXPECT_REGEX @: { .n = 0x[0-9a-f]+, .c = 120 } REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME args in uprobe store in map and access field PROG uprobe:./testprogs/uprobe_test:uprobeFunction1 { @ = args; print(@.c); exit(); } EXPECT 120 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME args in uprobe as a map key PROG uprobe:./testprogs/uprobe_test:uprobeFunction1 { @[args] = 1; exit(); } EXPECT_REGEX @[{ .n = 0x[0-9a-f]+, .c = 120 }]: 1 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME jiffies PROG i:ms:1 { printf("SUCCESS %llu\n", jiffies); exit(); } EXPECT_REGEX SUCCESS [0-9]+ REQUIRES_FEATURE jiffies64 MIN_KERNEL 5.9 NAME ustack builtin with stack_mode config RUN {{BPFTRACE}} -e 'config = { stack_mode=raw } uprobe:./testprogs/uprobe_test:uprobeFunction1 { @c[ustack] = 1; exit(); }' EXPECT_REGEX ^@c\[\n[0-9a-f]+$ AFTER ./testprogs/uprobe_test NAME kstack builtin with stack_mode config RUN {{BPFTRACE}} -e 'config = { stack_mode=raw } k:do_nanosleep { @c[kstack] = 1; exit(); }' EXPECT_REGEX ^@c\[\n[0-9a-f]+$ AFTER ./testprogs/syscall nanosleep 1e8 bpftrace-0.23.2/tests/runtime/call000066400000000000000000000607131477746507000170770ustar00rootroot00000000000000NAME printf PROG i:ms:1 { printf("hi!\n"); exit();} EXPECT hi! NAME printf_long_fmt PROG i:ms:1 { printf("hi abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz!\n"); exit();} EXPECT hi abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz! NAME printf_argument PROG i:ms:1 { printf("value: %dms100\n", 100); exit();} EXPECT value: 100ms100 NAME printf_llu PROG BEGIN { @start = nsecs; } i:s:1 { printf("Elapsed time: %llus\n", (nsecs - @start)/1000000000); clear(@start); exit();} EXPECT Elapsed time: 1s NAME printf_argument_alignment RUN {{BPFTRACE}} -e 'struct Foo { int a; char b[10]; } uprobe:testprogs/uprobe_test:uprobeFunction2 { $foo = (struct Foo *)arg0; $foo2 = (struct Foo *)arg1; printf("%d %s %d %s\n", $foo->a, $foo->b, $foo2->a, $foo2->b) }' -c ./testprogs/uprobe_test EXPECT 123 hello 456 world NAME printf_more_arguments PROG BEGIN { printf("%dst: %sa; %dnd: %sb;; %drd: %sc;;; %dth: %sd;;;;\n", 1, "a", 2, "ab", 3, "abc", 4, "abcd"); exit(); } EXPECT 1st: aa; 2nd: abb;; 3rd: abcc;;; 4th: abcdd;;;; NAME printf_length_modifiers PROG BEGIN { $x = 0x12345678abcdef; printf("%hhx %hx %x %jx\n", $x, $x, $x, $x); exit(); } EXPECT ef cdef 78abcdef 12345678abcdef NAME printf_char PROG BEGIN { printf("%c%c%c%c\n", 0x41, 0x42, 0x43, 0x44); exit(); } EXPECT ABCD NAME printf_enum_symbolize PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; BEGIN { printf("%d %s %d %s %d %s\n", ONE, ONE, TWO, TWO, OTHER, OTHER); exit() } EXPECT 1 ONE 2 TWO 99999 OTHER NAME printf_enum_symbolize_var PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; BEGIN { $one = ONE; $two = TWO; $other = OTHER; printf("%d %s %d %s %d %s\n", $one, $one, $two, $two, $other, $other); exit() } EXPECT 1 ONE 2 TWO 99999 OTHER NAME printf_enum_symbolize_map PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; BEGIN { @one = ONE; @two = TWO; @other = OTHER; printf("%d %s %d %s %d %s\n", @one, @one, @two, @two, @other, @other); exit() } EXPECT 1 ONE 2 TWO 99999 OTHER NAME printf_enum_symbolize_width PROG enum Foo { A, B, C, }; BEGIN { printf("%-5s %5s %s\n", A, B, C); exit() } EXPECT A B C NAME printf_enum_symbolize_tracepoint PROG tracepoint:skb:kfree_skb { $r = args->reason; printf("%d %s\n", args->reason, $r); exit() } EXPECT_REGEX ^\d+ [A-Z_]+$ AFTER ping localhost -c 5 TIMEOUT 5 NAME printf_enum_symbolize_cast PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; BEGIN { printf("%d %s\n", ONE, (enum Foo)1); exit() } EXPECT 1 ONE NAME printf_invalid_enum_symbolize_cast PROG enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; BEGIN { $x = 100; printf("%d %s\n", ONE, (enum Foo)$x); exit() } EXPECT 1 100 NAME time PROG i:ms:1 { time("%H:%M:%S\n"); exit();} EXPECT_REGEX [0-9]*:[0-9]*:[0-9]* NAME time_short PROG i:ms:1 { time("%H-%M:%S\n"); exit();} EXPECT_REGEX [0-9]*-[0-9]* NAME join RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo '_A_'"); } t:syscalls:sys_enter_execve { join(args.argv); exit();}' EXPECT _A_ NAME join_delim RUN {{BPFTRACE}} --unsafe -e 'i:ms:1 { system("echo '_A_'"); } t:syscalls:sys_enter_execve { join(args.argv, ","); exit();}' EXPECT _A_ NAME str PROG t:syscalls:sys_enter_execve { printf("P: %s\n", str(args.filename)); exit();} AFTER ./testprogs/syscall execve ./testprogs/true EXPECT_REGEX P: /*. NAME str_truncated PROG t:syscalls:sys_enter_execve { printf("P: %s\n", str(args.filename)); exit();} ENV BPFTRACE_MAX_STRLEN=5 AFTER ./testprogs/syscall execve ./testprogs/true &>/dev/null EXPECT_REGEX P: /...\.\. NAME explicit string truncation PROG tracepoint:syscalls:sys_enter_execve / str(args.argv[0]) == "./testprogs/true" / { print(str(args.argv[1], 5)); exit(); } AFTER ./testprogs/true zztest EXPECT zzte NAME str_truncated_custom PROG t:syscalls:sys_enter_execve { printf("P: %s\n", str(args.filename)); exit();} ENV BPFTRACE_MAX_STRLEN=5 BPFTRACE_STR_TRUNC_TRAILER=_xxx AFTER ./testprogs/syscall execve ./testprogs/true &>/dev/null EXPECT_REGEX P: /..._xxx NAME str_big PROG t:syscalls:sys_enter_execve { @[str(args.filename)] = count() } ENV BPFTRACE_MAX_STRLEN=9999 EXPECT_REGEX @\[/X{5555}\]: 1 AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)") REQUIRES_FEATURE probe_read_kernel # We rely on the timeout to terminate the script TIMEOUT 1 NAME str_big_strncmp PROG t:syscalls:sys_enter_execve { if (strncmp(str(args.filename), "/XXX", 4)) { print("matched"); exit(); } } ENV BPFTRACE_MAX_STRLEN=9999 EXPECT matched AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)") REQUIRES_FEATURE probe_read_kernel NAME str_big_printf PROG t:syscalls:sys_enter_execve { printf("%s\n", str(args.filename)) } ENV BPFTRACE_MAX_STRLEN=9999 EXPECT_REGEX /X{5555} AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)") REQUIRES_FEATURE probe_read_kernel NAME str_big_print PROG t:syscalls:sys_enter_execve { print(str(args.filename)) } ENV BPFTRACE_MAX_STRLEN=9999 EXPECT_REGEX /X{5555} AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)") REQUIRES_FEATURE probe_read_kernel NAME str_big_read PROG BEGIN { @s = str(0); @ss = @s; print("success!"); exit() } ENV BPFTRACE_MAX_STRLEN=9999 EXPECT success! REQUIRES_FEATURE probe_read_kernel TIMEOUT 1 NAME str_big_tuple PROG t:syscalls:sys_enter_execve { print((1, (2, str(args.filename)))) } ENV BPFTRACE_MAX_STRLEN=9999 EXPECT_REGEX \(1, \(2, /X{5555}\)\) AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)") REQUIRES_FEATURE probe_read_kernel TIMEOUT 1 NAME tuple_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { print((1, (2, "XXXX"))) } EXPECT (1, (2, XXXX)) NAME str_scratch_buf PROG config = { on_stack_limit = 0 } t:syscalls:sys_enter_execve { print(str(args.filename)) } EXPECT /XXXX AFTER ./testprogs/syscall execve /XXXX REQUIRES_FEATURE probe_read_kernel TIMEOUT 1 NAME fmt_str_args_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { printf("%s %d\n", "XXXX", 1); exit() } EXPECT XXXX 1 NAME map_val_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { @x = 1; @y = @x; print(@y); exit() } EXPECT @y: 1 NAME variable_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { $x = "XXXX"; $y = $x; print($y); exit() } EXPECT XXXX # Verify enough buffer space is allocated for same variable name in multiple scopes NAME variable_for_loop_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { @[1] = 1; for ($kv : @) { $y = $kv } for ($kv : @) { $y = $kv; print($y) } exit() } EXPECT (1, 1) # Verify enough buffer space is allocated for same variable name in multiple subprograms NAME variable_probe_subprog_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { $x = "XXXX" } i:ms:100 { $x = "YYYY"; exit(); }END { $x = "ZZZZ"; print($x) } EXPECT ZZZZ NAME scalar_map_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { @x = "xxxx"; print(@x); exit() } EXPECT @x: xxxx NAME map_key_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { @x[1, 2] = 1; @[1, 1] = 3; @x[2, 1] = 2; delete(@x[1, 2]); delete(@x, (1, 1)); print(@x); exit() } EXPECT @x[2, 1]: 2 NAME hist_map_key_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { @["ok_key"] = hist(1); exit() } EXPECT @[ok_key]: NAME hist_scalar_map_key_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { @ = hist(1); exit() } EXPECT @: NAME lhist_map_key_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { @["ok_key"] = lhist(2,0,10,2); exit() } EXPECT @[ok_key]: NAME lhist_scalar_map_key_scratch_buf PROG config = { on_stack_limit = 0 } BEGIN { @ = lhist(2,0,10,2); exit() } EXPECT @: NAME str_big_for PROG t:syscalls:sys_enter_execve { @map[str(args.filename)] = str(args.filename); for ($kv : @map) { print($kv); } } ENV BPFTRACE_MAX_STRLEN=9999 EXPECT_REGEX \(/X{5555}, /X{5555}\) AFTER ./testprogs/syscall execve /$(python3 -c "print('X'*5555)") REQUIRES_FEATURE probe_read_kernel TIMEOUT 1 NAME buf RUN {{BPFTRACE}} -e 'struct MyStruct { const char* a; char b[4]; uint8_t c[4]; int d[4]; uint16_t e[4]; uint64_t f[4] }; u:./testprogs/complex_struct:func { $s = (struct MyStruct *)arg0; printf("P: %r-%r-%r-%r-%r-%r\n", buf($s->a, 4), buf($s->b, 4), buf($s->c), buf($s->d), buf($s->e), buf($s->f)); exit(); }' -c ./testprogs/complex_struct EXPECT P: \x09\x08\x07\x06-\x05\x04\x03\x02-\x01\x02\x03\x04-\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00-\x09\x00\x0a\x00\x0b\x00\x0c\x00-\x0d\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00 ARCH x86_64|ppc64le|aarch64|armv7l NAME buf RUN {{BPFTRACE}} -e 'struct MyStruct { const char* a; char b[4]; uint8_t c[4]; int d[4]; uint16_t e[4]; uint64_t f[4] }; u:./testprogs/complex_struct:func { $s = (struct MyStruct *)arg0; printf("P: %r-%r-%r-%r-%r-%r\n", buf($s->a, 4), buf($s->b, 4), buf($s->c), buf($s->d), buf($s->e), buf($s->f)); exit(); }' -c ./testprogs/complex_struct EXPECT P: \x09\x08\x07\x06-\x05\x04\x03\x02-\x01\x02\x03\x04-\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08-\x00\x09\x00\x0a\x00\x0b\x00\x0c-\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x10 ARCH s390x|ppc64 NAME buf_map_key PROG i:ms:100 { @[buf("ok_key", 6)] = 1; exit(); } EXPECT @[ok_key]: 1 NAME buf_map_multikey PROG BEGIN { @[buf("ok_key", 7), 1] = hist(1); exit(); } EXPECT @[ok_key\x00, 1]: NAME buf_hist_map_key PROG BEGIN { @[buf("ok_key", 7)] = hist(1); exit(); } EXPECT @[ok_key\x00]: NAME buf_map_value PROG i:ms:100 { @ = buf("ok_value", 8); exit(); } EXPECT @: ok_value NAME buf_no_ascii PROG BEGIN { printf("%rx", buf("Hello\0", 6)); exit(); } EXPECT \x48\x65\x6c\x6c\x6f\x00 NAME buf_no_ascii_no_escaping PROG BEGIN { printf("%rh", buf("Hello\0", 6)); exit(); } EXPECT 48 65 6c 6c 6f 00 NAME ksym PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("ip"))); exit();} EXPECT do_nanosleep ARCH x86_64 AFTER ./testprogs/syscall nanosleep 1e8 NAME ksym PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("pswaddr"))); exit();} EXPECT do_nanosleep ARCH s390x AFTER ./testprogs/syscall nanosleep 1e8 NAME ksym PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("pc"))); exit();} EXPECT do_nanosleep ARCH aarch64|armv7l AFTER ./testprogs/syscall nanosleep 1e8 NAME ksym PROG kprobe:do_nanosleep { printf("%s\n", ksym(reg("nip"))); exit();} EXPECT do_nanosleep ARCH ppc64|ppc64le AFTER ./testprogs/syscall nanosleep 1e8 NAME system RUN {{BPFTRACE}} --unsafe -e 'i:ms:100 { system("echo 'ok_system'"); exit();}' EXPECT ok_system NAME system_more_args RUN {{BPFTRACE}} --unsafe -e 'i:ms:100 { system("echo %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7); exit();}' EXPECT 1 2 3 4 5 6 7 NAME count PROG i:ms:100 { @ = count(); exit();} EXPECT_REGEX @:\s[0-9]+ NAME sum PROG BEGIN { @sum = sum(1); @sum = sum(2); @sum = sum(3); exit();} EXPECT @sum: 6 NAME avg PROG BEGIN { @avg = avg(1); @avg = avg(2); @avg = avg(3); exit();} EXPECT @avg: 2 NAME min PROG BEGIN { @min = min(2); @min = min(1); @min = min(3); exit();} EXPECT @min: 1 NAME max PROG BEGIN { @max = max(1); @max = max(3); @max = max(2); exit();} EXPECT @max: 3 NAME stats PROG BEGIN { @stats = stats(1); @stats = stats(2); @stats = stats(3); exit();} EXPECT @stats: count 3, average 2, total 6 NAME hist PROG BEGIN { @=hist(-1); @=hist(2); @=hist(3); @=hist(7); @=hist(20); exit();} EXPECT_FILE runtime/outputs/hist.txt TIMEOUT 1 NAME hist_2_values PROG BEGIN { @=hist(3);@=hist(20);exit();} EXPECT_FILE runtime/outputs/hist_2_values.txt TIMEOUT 1 NAME hist_10g PROG BEGIN { @ = hist(10 * 1024 * 1024 * 1024); exit(); } EXPECT_JSON runtime/outputs/hist_10g.json TIMEOUT 1 NAME lhist PROG BEGIN { @=lhist(2,0,10,2); @=lhist(3,0,10,2); @=lhist(7,0,10,2); @=lhist(-1,0,10,2); @=lhist(11,0,10,2); exit()} EXPECT_FILE runtime/outputs/lhist.txt NAME kstack PROG k:do_nanosleep { printf("%s\n%s\n", kstack(), kstack(1)); exit(); } EXPECT Attaching 1 probe... AFTER ./testprogs/syscall nanosleep 1e8 NAME kstack perf mode PROG k:do_nanosleep { printf("%s\n", kstack(perf, 1)); exit(); } EXPECT Attaching 1 probe... AFTER ./testprogs/syscall nanosleep 1e8 NAME ustack PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n%s\n", ustack(), ustack(1)); exit(); } EXPECT Attaching 1 probe... AFTER ./testprogs/uprobe_loop NAME ustack_stack_mode_env_bpftrace PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n", ustack(1)); exit(); } ENV BPFTRACE_STACK_MODE=bpftrace EXPECT_REGEX ^\s+uprobeFunction1\+[0-9]+$ AFTER ./testprogs/uprobe_loop NAME ustack_stack_mode_env_perf PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n", ustack(1)); exit(); } ENV BPFTRACE_STACK_MODE=perf EXPECT_REGEX ^\s+[0-9a-f]+ uprobeFunction1\+[0-9]+ \(.*/uprobe_loop\)$ AFTER ./testprogs/uprobe_loop NAME ustack_stack_mode_env_raw PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n", ustack(1)); exit(); } ENV BPFTRACE_STACK_MODE=raw EXPECT_REGEX ^[\da-fA-F]+$ AFTER ./testprogs/uprobe_loop NAME ustack_stack_mode_env_override PROG u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n", ustack(raw, 1)); exit(); } ENV BPFTRACE_STACK_MODE=perf EXPECT_REGEX ^[\da-fA-F]+$ AFTER ./testprogs/uprobe_loop NAME ustack_elf_symtable ENV BPFTRACE_CACHE_USER_SYMBOLS=PER_PROGRAM PROG uprobe:./testprogs/uprobe_symres_exited_process:test { print(ustack); exit(); } EXPECT_REGEX ^\s+test\+[0-9]+\s+test2\+[0-9]+\s+main\+[0-9]+ AFTER ./testprogs/disable_aslr ./testprogs/uprobe_symres_exited_process # Required to skip function prologue REQUIRES_FEATURE dwarf NAME cat PROG i:ms:1 { cat("/proc/uptime"); exit();} EXPECT_REGEX [0-9]*.[0-9]* [0-9]*.[0-9]* NAME cat_more_args PROG i:ms:1 { cat("/%s%s%s%s/%s%s%s", "p", "r", "o", "c", "u", "p", "time"); exit();} EXPECT_REGEX [0-9]*.[0-9]* [0-9]*.[0-9]* NAME uaddr RUN {{BPFTRACE}} -e 'uprobe:testprogs/uprobe_test:uprobeFunction1 { printf("0x%lx -- 0x%lx\n", *uaddr("GLOBAL_A"), *uaddr("GLOBAL_C")); exit(); }' -c ./testprogs/uprobe_test EXPECT 0x55555555 -- 0x33333333 NAME ntop static ip PROG i:ms:100 { printf("IP: %s, %s, %s, %s\n", ntop(1), ntop(0x0100007f), ntop(0xffff0000), ntop(0x0000ffff)); exit() } EXPECT IP: 1.0.0.0, 127.0.0.1, 0.0.255.255, 255.255.0.0 ARCH x86_64|aarch64|ppc64le|armv7l NAME ntop static ip PROG i:ms:100 { printf("IP: %s, %s, %s, %s\n", ntop(0x01000000), ntop(0x7f000001), ntop(0x0000ffff), ntop(0xffff0000)); exit() } EXPECT IP: 1.0.0.0, 127.0.0.1, 0.0.255.255, 255.255.0.0 ARCH s390x|ppc64 NAME pton ipv4 PROG i:ms:100 { printf("IP: %s\n", ntop(2, pton("127.0.0.1"))); exit(); } EXPECT IP: 127.0.0.1 NAME pton ipv6 PROG i:ms:100 { printf("IP: %s\n", ntop(10, pton("::1"))); exit(); } EXPECT IP: ::1 NAME usym PROG i:ms:100 { @=usym(1); @a=@; exit(); } EXPECT @a: 0x1 NAME print_non_map PROG BEGIN { $x = 5; print($x); exit() } EXPECT 5 TIMEOUT 1 NAME print_non_map_builtin PROG BEGIN { print(comm); exit() } EXPECT bpftrace TIMEOUT 1 NAME print_non_map_tuple PROG BEGIN { $t = (1, 2, "string"); print($t); exit() } EXPECT (1, 2, string) TIMEOUT 1 NAME print_non_map_array PROG struct A { int x[4]; } uprobe:./testprogs/array_access:test_struct { $a = ((struct A *) arg0)->x; print($a); exit(); } EXPECT [1,2,3,4] AFTER ./testprogs/array_access NAME print_non_map_multi_dimensional_array PROG struct B { int y[2][2]; } uprobe:./testprogs/array_access:test_struct { $b = ((struct B *) arg1)->y; print($b); exit(); } EXPECT [[5,6],[7,8]] AFTER ./testprogs/array_access NAME print_non_map_struct PROG struct Foo { int m; int n; } uprobe:./testprogs/simple_struct:func { $f = *((struct Foo *) arg0); print($f); exit(); } EXPECT { .m = 2, .n = 3 } AFTER ./testprogs/simple_struct NAME print_non_map_struct_fentry PROG fentry:vfs_open { print(*args.path); exit(); } EXPECT_REGEX { .mnt = 0x[0-9a-f]+, .dentry = 0x[0-9a-f]+ } REQUIRES_FEATURE fentry AFTER ./testprogs/syscall open NAME print_map_item PROG BEGIN { @x[1] = 5; print(@x[1]); exit() } EXPECT 5 TIMEOUT 1 NAME print_map_item_tuple PROG BEGIN { @x[1] = "hi"; print((1, 2, @x[1])); exit() } EXPECT (1, 2, hi) TIMEOUT 1 NAME strftime PROG BEGIN { $ts = strftime("%m/%d/%y", nsecs); printf("%s\n", $ts); exit(); } EXPECT_REGEX [0-9]{2}\/[0-9]{2}\/[0-9]{2} NAME strftime_as_map_key PROG BEGIN { @[strftime("%m/%d/%y", nsecs)] = 1; exit();} EXPECT_REGEX \[[0-9]{2}\/[0-9]{2}\/[0-9]{2}\]: 1 NAME strftime_as_map_value PROG BEGIN { @[nsecs] = strftime("%m/%d/%y", nsecs); exit();} EXPECT_REGEX @\[[0-9]*\]: [0-9]{2}\/[0-9]{2}\/[0-9]{2} # Output two microsecond timestamps, 123000 nsecs apart. Use python to evaluate and verify there's a 123us delta NAME strftime_microsecond_extension RUN {{BPFTRACE}} -e 'BEGIN { printf("%s - %s\n", strftime("%s.%f", 123000), strftime("%s.%f", 0)); exit(); }' | tail -n +3 | bc EXPECT .000123 TIMEOUT 1 # Similar to above test but test that rolling over past 1s works as expected NAME strftime_microsecond_extension_rollover RUN {{BPFTRACE}} -e 'BEGIN { printf("%s - %s\n", strftime("%s.%f", 1000123000), strftime("%s.%f", 0)); exit(); }' | tail -n +3 | bc EXPECT 1.000123 TIMEOUT 1 NAME print_avg_map_args PROG BEGIN { @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 2, 10); clear(@); exit(); } EXPECT_FILE runtime/outputs/print_avg_map_args.txt TIMEOUT 1 NAME print_avg_map_with_large_top PROG BEGIN { @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 10, 10); clear(@); exit(); } EXPECT_FILE runtime/outputs/print_avg_map_with_large_top.txt TIMEOUT 1 NAME print_hist_with_top_arg PROG BEGIN { print("BEGIN"); @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 2); print("END"); clear(@); exit(); } EXPECT_REGEX BEGIN\n@\[2\]:(.*\n)+@\[3\]:(.*\n)+END TIMEOUT 1 NAME print_hist_with_large_top_arg PROG BEGIN { print("BEGIN"); @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 10); print("END"); clear(@); exit(); } EXPECT_REGEX BEGIN\n@\[1\]:(.*\n)+@\[2\]:(.*\n)+@\[3\]:(.*\n)+END TIMEOUT 1 NAME path RUN {{BPFTRACE}} -ve 'fentry:security_file_open { if (!strncmp(path(args.file->f_path), "/tmp/bpftrace_runtime_test_syscall_gen_open_temp", 49)) { printf("OK\n"); exit(); } }' EXPECT OK REQUIRES_FEATURE dpath fentry AFTER TMPDIR=/tmp ./testprogs/syscall open NAME path_with_optional_size RUN {{BPFTRACE}} -ve 'fentry:security_file_open { $p = path(args.file->f_path, 48); if ( sizeof($p) == 48 && !strncmp($p, "tmp/bpftrace_runtime_test_syscall_gen_open_temp", 48)) { printf("OK\n"); exit(); } }' EXPECT OK REQUIRES_FEATURE dpath fentry AFTER TMPDIR=/tmp ./testprogs/syscall open # The extra prints are here to confirm that we report on the correct helper, not # just the first or the last one. NAME path_in_unsupported_fentry PROG fentry:vfs_read { print("a"); print(path(args.file->f_path)); print("b"); } EXPECT stdin:1:31-60: ERROR: helper bpf_d_path not allowed in probe EXPECT fentry:vfs_read { print("a"); print(path(args.file->f_path)); print("b"); } EXPECT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ REQUIRES_FEATURE dpath REQUIRES_FEATURE fentry WILL_FAIL AFTER ./testprogs/syscall read NAME macaddr RUN {{BPFTRACE}} -e 'struct MyStruct { const char* ignore; char mac[6]; }; u:./testprogs/complex_struct:func { $s = ((struct MyStruct *)arg0); printf("P: %s\n", macaddr($s->mac)); exit(); }' -c ./testprogs/complex_struct EXPECT P: 05:04:03:02:01:02 NAME macaddr as map key RUN {{BPFTRACE}} -e 'struct MyStruct { const char* ignore; char mac[6]; }; u:./testprogs/complex_struct:func { $s = ((struct MyStruct *)arg0); @[macaddr($s->mac)] = 1; exit(); }' -c ./testprogs/complex_struct EXPECT @[05:04:03:02:01:02]: 1 NAME macaddr as map value RUN {{BPFTRACE}} -e 'struct MyStruct { const char* ignore; char mac[6]; }; u:./testprogs/complex_struct:func { $s = ((struct MyStruct *)arg0); @[1] = macaddr($s->mac); exit(); }' -c ./testprogs/complex_struct EXPECT @[1]: 05:04:03:02:01:02 NAME iter:task PROG iter:task { printf("comm: %s\n", ctx->task->comm); exit(); } EXPECT comm: bpftrace REQUIRES_FEATURE iter NAME iter:task_file PROG iter:task_file { printf("comm: %s\n", ctx->task->comm); exit(); } EXPECT comm: bpftrace REQUIRES_FEATURE iter NAME iter:task_vma PROG iter:task_vma { printf("comm: %s\n", ctx->task->comm); exit(); } EXPECT comm: bpftrace REQUIRES_FEATURE iter NAME iter printf multiple values PROG iter:task { printf("%s: %d\n", "hello", 0); exit(); } EXPECT hello: 0 REQUIRES_FEATURE iter NAME cgroup_path PROG BEGIN { print(cgroup_path(cgroup & ((1 << 32) - 1))); exit(); } EXPECT_REGEX ([a-z]*:\/.*;)*[a-z]*:\/.* REQUIRES grep -q '^cgroup2' /proc/mounts MIN_KERNEL 4.18 NAME cgroup_path printf PROG BEGIN { printf("path: %s", cgroup_path(cgroup & ((1 << 32) - 1))); exit(); } EXPECT_REGEX path: ([a-z]*:\/.*;)*[a-z]*:\/.* REQUIRES grep -q '^cgroup2' /proc/mounts MIN_KERNEL 4.18 NAME strerror RUN {{BPFTRACE}} --include "errno.h" -e 'BEGIN { print(strerror(EPERM)); exit(); }' EXPECT Operation not permitted NAME bswap_int64 RUN {{BPFTRACE}} -e 'BEGIN { printf("%llx", 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("%llx", 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 NAME skboutput RUN {{BPFTRACE}} -e 'fentry:__dev_queue_xmit { $ret = skboutput("skb.pcap", args.skb, args.skb->len, 14); printf("ret: %d\n", $ret); exit(); }' SETUP ip netns add bpftrace-test && ip -netns bpftrace-test link set lo up AFTER ip netns exec bpftrace-test ./testprogs/syscall connect 127.0.0.1 80 CLEANUP ip netns delete bpftrace-test EXPECT ret: 0 REQUIRES_FEATURE fentry REQUIRES_FEATURE skboutput MIN_KERNEL 5.5 NAME debugf RUN echo > /sys/kernel/debug/tracing/trace; {{BPFTRACE}} -e 'i:ms:1 { debugf("debugf"); exit();}'; cat /sys/kernel/debug/tracing/trace EXPECT_REGEX bpf_trace_printk: debugf TIMEOUT 3 NAME debugf_with_arguments RUN echo > /sys/kernel/debug/tracing/trace; {{BPFTRACE}} -e 'i:ms:1 { debugf("debugf %s %d %x", "a", 1, 16); exit();}'; cat /sys/kernel/debug/tracing/trace EXPECT_REGEX bpf_trace_printk: debugf a 1 10 TIMEOUT 3 NAME debugf_with_seq_printf RUN echo > /sys/kernel/debug/tracing/trace; {{BPFTRACE}} -e 'i:ms:1 { printf("printf %d", 0); debugf("debugf %d", 1); exit();}'; cat /sys/kernel/debug/tracing/trace EXPECT_REGEX bpf_trace_printk: debugf 1 TIMEOUT 3 NAME nsecs PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs()); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME nsecs_monotonic PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs(monotonic)); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME nsecs_boot PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs(boot)); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME nsecs_tai PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs(tai)); exit(); } EXPECT_REGEX SUCCESS [0-9]+ REQUIRES_FEATURE get_tai_ns MIN_KERNEL 6.1 NAME nsecs_sw_tai PROG i:ms:1 { printf("SUCCESS %llu\n", nsecs(sw_tai)); exit(); } EXPECT_REGEX SUCCESS [0-9]+ NAME map len PROG BEGIN { @[0] = 0; @[1] = 1; printf("map len: %d\n", len(@)); exit(); } EXPECT map len: 2 TIMEOUT 3 NAME map lencmp PROG BEGIN { @[1] = 1; @[2] = 2; $count = len(@); if ($count > 1) { print("true"); } exit(); } EXPECT true TIMEOUT 3 NAME map len keyless PROG BEGIN { @ = 0; printf("map len: %d\n", len(@)); exit(); } EXPECT map len: 1 NAME percpu_kaddr PROG BEGIN { printf("processes: %d\n", *percpu_kaddr("process_counts", 0)); exit(); } EXPECT_REGEX processes: -?[0-9]+ NAME percpu_kaddr this cpu PROG BEGIN { if (*percpu_kaddr("process_counts") == *percpu_kaddr("process_counts", cpu)) { printf("SUCCESS\n") } exit(); } EXPECT SUCCESS NAME percpu_kaddr field access PROG BEGIN { $runq = ((struct rq *)percpu_kaddr("runqueues", 0)); if ($runq != 0) { printf("nr_running: %d\n", $runq->nr_running) } exit() } EXPECT_REGEX nr_running: [0-9]+ NAME percpu_kaddr field access no NULL check PROG BEGIN { $runq = ((struct rq *)percpu_kaddr("runqueues", 0)); printf("nr_running: %d\n", $runq->nr_running); exit() } EXPECT stdin:1:17-60: ERROR: helper bpf_per_cpu_ptr: result needs to be null-checked before accessing fields BEGIN { $runq = ((struct rq *)percpu_kaddr("runqueues", 0)); printf("nr_running: %d\n", $runq->nr_running); exit() } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ WILL_FAIL NAME clear on scalar map prevents printing PROG BEGIN { @xx = 5; clear(@xx); exit() } EXPECT_NONE @xx: 5 NAME ustack len PROG u:./testprogs/uprobe_loop:uprobeFunction1 { $x = ustack(3); @ = len($x); exit(); } EXPECT @: 3 AFTER ./testprogs/uprobe_loop NAME kstack len PROG k:do_nanosleep { @ = len(kstack); exit() } EXPECT_REGEX @: \d+$ AFTER ./testprogs/syscall nanosleep 1e8 bpftrace-0.23.2/tests/runtime/complex_types000066400000000000000000000012671477746507000210560ustar00rootroot00000000000000NAME struct-array RUN {{BPFTRACE}} runtime/scripts/struct_array.bt -c ./testprogs/struct_array EXPECT 100 102 104 106 108 -- 1 3 5 7 9 -- 100 98 96 94 92 NAME struct-array with variables RUN {{BPFTRACE}} runtime/scripts/struct_array_vars.bt -c ./testprogs/struct_array EXPECT 100 102 104 106 108 -- 1 3 5 7 9 -- 100 98 96 94 92 -- 42 NAME struct-array with declared variables RUN {{BPFTRACE}} runtime/scripts/struct_let.bt -c ./testprogs/struct_array EXPECT 100 102 104 106 108 NAME pointer_to_pointer RUN {{BPFTRACE}} -e 'struct Foo { int a; char b[10]; } uprobe:./testprogs/ptr_to_ptr:function { $pp = (struct Foo **)arg0; printf("%d\n", (*$pp)->a); }' -c ./testprogs/ptr_to_ptr EXPECT 123 bpftrace-0.23.2/tests/runtime/config000066400000000000000000000026771477746507000174360ustar00rootroot00000000000000NAME config as env var RUN {{BPFTRACE}} -e 'config = { BPFTRACE_STACK_MODE=raw } uprobe:./testprogs/uprobe_test:uprobeFunction1 { printf("%s", ustack(1)); exit(); }' EXPECT_REGEX ^\s+[0-9a-f]+$ AFTER ./testprogs/uprobe_test NAME config short name RUN {{BPFTRACE}} -e 'config = { stack_mode=raw } uprobe:./testprogs/uprobe_test:uprobeFunction1 { printf("%s", ustack(1)); exit(); }' EXPECT_REGEX ^\s+[0-9a-f]+$ AFTER ./testprogs/uprobe_test NAME env var takes precedence RUN {{BPFTRACE}} -e 'config = { BPFTRACE_STACK_MODE=perf } uprobe:./testprogs/uprobe_test:uprobeFunction1 { printf("%s", ustack(1)); exit(); }' ENV BPFTRACE_STACK_MODE=raw EXPECT_REGEX ^\s+[0-9a-f]+$ AFTER ./testprogs/uprobe_test NAME bad config RUN {{BPFTRACE}} -e 'config = { bad_config=raw } BEGIN {}' EXPECT stdin:1:12-23: ERROR: Unrecognized config variable: bad_config WILL_FAIL NAME env only config RUN {{BPFTRACE}} -e 'config = { debug_output=1 } BEGIN {}' EXPECT stdin:1:12-25: ERROR: debug_output can only be set as an environment variable WILL_FAIL NAME maps are printed by default PROG BEGIN { @["test"] = count(); exit(); } EXPECT @[test]: 1 NAME maps can be disabled PROG config = { print_maps_on_exit=0 } BEGIN { @["test"] = count(); exit(); } EXPECT_NONE @[test]: 1 NAME scalar maps are printed by default PROG BEGIN { @test = 1; exit(); } EXPECT @test: 1 NAME scalar maps can be disabled PROG config = { print_maps_on_exit=0 } BEGIN { @test = 1; exit(); } EXPECT_NONE @test: 1 bpftrace-0.23.2/tests/runtime/dwarf000066400000000000000000000175041477746507000172670ustar00rootroot00000000000000NAME list uprobe args - basic type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test:main' EXPECT int argc REQUIRES_FEATURE dwarf NAME list uprobe args - pointer type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test:uprobeFunction1' EXPECT int * n REQUIRES_FEATURE dwarf NAME list uprobe args - struct pointer type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test:uprobeFunction2' EXPECT struct Foo * foo1 REQUIRES_FEATURE dwarf NAME list uprobe args - anonymous param type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test:uprobeFunction3' EXPECT enum { A, B, C } e union { int a; char b; } u REQUIRES bash -c "exit 1" # SKIP: anonymous parameter type not supported #3083 REQUIRES_FEATURE dwarf NAME list uprobe args - class reference type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test_cxx:cpp:uprobeFunction1' EXPECT int & x Foo & foo Bar & bar REQUIRES_FEATURE dwarf NAME list uprobe args - array reference type RUN {{BPFTRACE}} -lv 'uprobe:./testprogs/uprobe_test_cxx:cpp:uprobeArray' EXPECT int (&)[10] array REQUIRES_FEATURE dwarf NAME uprobe arg by name - char PROG uprobe:./testprogs/uprobe_test:uprobeFunction1 { printf("c = %c\n", args.c); exit(); } EXPECT c = x REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME uprobe arg by name - pointer PROG uprobe:./testprogs/uprobe_test:uprobeFunction1 { printf("n = %d\n", *(args.n)); exit(); } EXPECT n = 13 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME uprobe arg by name - struct PROG uprobe:./testprogs/uprobe_test:uprobeFunction2 { printf("foo1->a = %d\n", args.foo1->a); exit(); } EXPECT foo1->a = 123 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test # NAME uprobe arg by index - 128-bits integer # PROG uprobe:./testprogs/uprobe_test:uprobeFunctionUint128 { printf("x = %x\ny = %x\nz = %x\nw = %x\n", arg0, arg1, arg2, arg3); exit(); } # EXPECT x = 9abcdef0 # y = efefefef # z = cdcdcdcd # w = abababab # REQUIRES_FEATURE dwarf # AFTER ./testprogs/uprobe_test # NAME uprobe arg by name - 128-bits integer # PROG uprobe:./testprogs/uprobe_test:uprobeFunctionUint128 { printf("x = %x\ny = %x\nz = %x\nw = %x\n", args.x, args.y, args.z, args.w); exit(); } # EXPECT x = 9abcdef0 # y = efefefef # z = cdcdcdcd # w = abababab # REQUIRES_FEATURE dwarf # AFTER ./testprogs/uprobe_test NAME uprobe arg by name - struct with 128-bits integer PROG uprobe:./testprogs/uprobe_test:uprobeFunction2 { printf("foo1->d = %x\n", args.foo1->d); exit(); } EXPECT foo1->d = 9abcdef0 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME uprobe without dwarf PROG config = { symbol_source = "symbol_table"; cache_user_symbols = "PER_PROGRAM"; } uprobe:./testprogs/uprobe_test:uprobeFunction1 { print(ustack); exit(); } EXPECT uprobeFunction1+0 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME uprobe inline without dwarf PROG config = { symbol_source = "symbol_table"; probe_inline = 1; } uprobe:./testprogs/inline_function:square { @count++ } uretprobe:./testprogs/inline_function:main { exit() } EXPECT @count: 1 REQUIRES_FEATURE dwarf AFTER ./testprogs/inline_function NAME uprobe skip inlined function PROG config = { probe_inline = 0 } uprobe:./testprogs/inline_function:square { @count++ } uretprobe:./testprogs/inline_function:main { exit() } EXPECT @count: 1 REQUIRES_FEATURE dwarf AFTER ./testprogs/inline_function NAME uprobe inlined function PROG config = { probe_inline = 1 } uprobe:./testprogs/inline_function:square { @count++ } uretprobe:./testprogs/inline_function:main { exit() } EXPECT @count: 3 REQUIRES_FEATURE dwarf AFTER ./testprogs/inline_function NAME uprobe inlined function - probe PROG config = { probe_inline = 1; cache_user_symbols = "PER_PROGRAM"; } uprobe:./testprogs/inline_function:square { printf("%s\n", probe); if (++@count == 3) { exit(); } } EXPECT uprobe:./testprogs/inline_function:square uprobe:./testprogs/inline_function:square uprobe:./testprogs/inline_function:square REQUIRES_FEATURE dwarf AFTER ./testprogs/inline_function NAME uprobe inlined function - func PROG config = { probe_inline = 1; cache_user_symbols = "PER_PROGRAM"; } uprobe:./testprogs/inline_function:square { printf("%s\n", func); if (++@count == 3) { exit(); } } EXPECT main main square REQUIRES_FEATURE dwarf AFTER ./testprogs/inline_function NAME uprobe inlined function - ustack PROG config = { probe_inline = 1; cache_user_symbols = "PER_PROGRAM"; } uprobe:./testprogs/inline_function:square { printf("%s\n", ustack); if (++@count == 3) { exit(); } } EXPECT_REGEX ^\n[ ]+main\+\d+$ EXPECT_REGEX ^\n[ ]+square\+\d+\n[ ]+main\+\d+$ REQUIRES_FEATURE dwarf AFTER ./testprogs/inline_function NAME print uprobe arg as deref pointer - struct PROG uprobe:./testprogs/uprobe_test:uprobeFunction2 { print(*args.foo1); exit(); } EXPECT { .a = 123, .b = hello, .c = [1,2,3], .d = 1311768467463790320 } REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME uprobe arg as reference - int PROG uprobe:./testprogs/uprobe_test_cxx:cpp:uprobeFunction1 { printf("x = %d\n", args.x); exit(); } EXPECT x = 42 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test_cxx NAME print uprobe arg as reference - struct PROG uprobe:./testprogs/uprobe_test_cxx:cpp:uprobeFunction1 { print(args.foo); exit(); } EXPECT_REGEX ^\{ \.a = 1, \.b = 2, \.c = 3, \.x = 0x[0-9a-f]{1,16} \}$ REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test_cxx NAME uprobe arg as reference - struct member PROG uprobe:./testprogs/uprobe_test_cxx:cpp:uprobeFunction1 { printf("foo.a = %d\nfoo.b = %d\nfoo.c = %d\nfoo.x = %d\n", args.foo.a, args.foo.b, args.foo.c, args.foo.x); exit(); } EXPECT foo.a = 1 foo.b = 2 foo.c = 3 foo.x = 42 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test_cxx NAME uprobe arg as reference - array PROG uprobe:./testprogs/uprobe_test_cxx:cpp:uprobeArray { printf("arr[0] = %d, arr[1] = %d, arr[9] = %d\n", args.array[0], args.array[1], args.array[9]); exit(); } EXPECT arr[0] = 1, arr[1] = 2, arr[9] = 10 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test_cxx # Checking backwards compatibility NAME uprobe args as pointer PROG uprobe:./testprogs/uprobe_test:uprobeFunction1 { printf("c = %c\n", args->c); exit(); } EXPECT c = x REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME struct field string PROG uprobe:./testprogs/uprobe_test:uprobeFunction2 { printf("foo1->b = %s\n", args.foo1->b); exit(); } EXPECT foo1->b = hello REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME struct field array PROG uprobe:./testprogs/uprobe_test:uprobeFunction2 { print(args.foo1->c); exit(); } EXPECT [1,2,3] REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME cast to struct PROG uprobe:./testprogs/uprobe_test:uprobeFunction2 { printf("foo1->a = %d\n", ((struct Foo *)arg0)->a); exit(); } EXPECT foo1->a = 123 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test NAME struct override PROG struct Foo { int b; } uprobe:./testprogs/uprobe_test:uprobeFunction2 { printf("foo1->b = %d\n", ((struct Foo *)arg0)->b); exit(); } EXPECT foo1->b = 123 REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_test # The function `str_has_prefix` is marked to be always inlined, so we'll get # more than one occurrence for it. # We first check that bpftrace attaches to more probes than the two defined in the script. # We also check that the offsets bpftrace attaches to are different from 0, # since they should be inline instances part of another function/symbol. NAME kprobe inlined function RUN {{BPFTRACE}} -ve \ 'config = { probe_inline = 1 } BEGIN { exit() } kprobe:str_has_prefix { }' EXPECT_NONE Attaching 1 probe... EXPECT_NONE Attaching 2 probes... EXPECT_REGEX_NONE ^bpf_attach_kprobe\(.*, 0, 0\)$ REQUIRES_FEATURE dwarf kernel_dwarf bpftrace-0.23.2/tests/runtime/engine/000077500000000000000000000000001477746507000174775ustar00rootroot00000000000000bpftrace-0.23.2/tests/runtime/engine/cmake_vars.py000066400000000000000000000001021477746507000221550ustar00rootroot00000000000000LIBBCC_BPF_CONTAINS_RUNTIME = bool(@LIBBCC_BPF_CONTAINS_RUNTIME@) bpftrace-0.23.2/tests/runtime/engine/main.py000066400000000000000000000076521477746507000210070ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse from datetime import timedelta import math import os import re import time from parser import TestParser, UnknownFieldError, RequiredFieldError from runner import Runner, ok, fail, warn TEST_FILTER = os.getenv("TEST_FILTER") def main(test_filter, allowlist_file, run_aot_tests): if not test_filter: test_filter = ".*" allowlist = set() if allowlist_file: with open(allowlist_file, 'r') as f: allowlist = { line.strip() for line in f if not line.startswith("#") } try: test_suite = sorted(TestParser.read_all(run_aot_tests)) test_suite = [ (n, sorted(t)) for n, t in test_suite ] except (UnknownFieldError, RequiredFieldError) as error: print(fail(str(error))) exit(1) # Apply filter filtered_suites = [] for fname, tests in test_suite: filtered_tests = [t for t in tests if re.search(test_filter, "{}.{}".format(fname, t.name))] if len(filtered_tests) != 0: filtered_suites.append((fname, filtered_tests)) test_suite = filtered_suites total_tests = 0 for fname, suite_tests in test_suite: total_tests += len(suite_tests) failed_tests = [] timeouted_tests = [] print(ok("[==========]") + " Running %d tests from %d test cases.\n" % (total_tests, len(test_suite))) start_time = time.time() skipped_tests = [] for fname, tests in test_suite: print(ok("[----------]") + " %d tests from %s" % (len(tests), fname)) for test in tests: if allowlist_file and (f"{fname}.{test.name}" not in allowlist): skipped_tests.append((fname, test, Runner.SKIP_NOT_IN_ALLOWLIST)) continue status = Runner.run_test(test) if Runner.skipped(status): skipped_tests.append((fname, test, status)) if Runner.failed(status): failed_tests.append("%s.%s" % (fname, test.name)) if Runner.timeouted(status): timeouted_tests.append("%s.%s" % (fname, test.name)) # TODO(mmarchini) elapsed time per test suite and per test (like gtest) print(ok("[----------]") + " %d tests from %s\n" % (len(tests), fname)) elapsed = time.time() - start_time total_tests -= len(skipped_tests) print(ok("[==========]") + " %d tests from %d test cases ran. (%s ms total)" % (total_tests, len(test_suite), math.ceil(elapsed * 1000))) print(ok("[ PASSED ]") + " %d tests." % (total_tests - len(failed_tests))) if skipped_tests: print(warn("[ SKIP ]") + " %d tests, listed below:" % len(skipped_tests)) for test_suite, test, status in skipped_tests: print(warn("[ SKIP ]") + " %s.%s (%s)" % (test_suite, test.name, Runner.skip_reason(test, status))) if failed_tests or timeouted_tests: print(fail("[ FAILED ]") + " %d tests, listed below:" % (len(failed_tests) + len(timeouted_tests))) for failed_test in failed_tests: print(fail("[ FAILED ]") + " %s" % failed_test) for timeouted_test in timeouted_tests: print(fail("[ TIMEOUT ]") + " %s" % timeouted_test) if failed_tests or timeouted_tests: exit(1) if __name__ == "__main__": parser = argparse.ArgumentParser(description='Runtime tests for bpftrace.') parser.add_argument('--filter', type=str, dest='test_filter', help='Run only specified runtime test. Format should be "."') parser.add_argument('--allowlist_file', type=str, help='Path to file containing tests to allow. Format is one test per line.') parser.add_argument('--run-aot-tests', action='store_true', help='Run ahead-of-time compilation tests. Note this would roughly double test time.') args = parser.parse_args() main(args.test_filter or TEST_FILTER, args.allowlist_file, args.run_aot_tests) bpftrace-0.23.2/tests/runtime/engine/parser.py000066400000000000000000000216101477746507000213450ustar00rootroot00000000000000#!/usr/bin/python3 from collections import namedtuple import os import platform DEFAULT_TIMEOUT = 5 class RequiredFieldError(Exception): pass class UnknownFieldError(Exception): pass class InvalidFieldError(Exception): pass class Expect: def __init__(self, expect, mode): self.expect = expect self.mode = mode TestStruct = namedtuple( 'TestStruct', [ 'name', 'run', 'prog', 'expects', 'has_exact_expect', 'timeout', 'befores', 'after', 'setup', 'cleanup', 'suite', 'kernel_min', 'kernel_max', 'requirement', 'env', 'arch', 'feature_requirement', 'neg_feature_requirement', 'will_fail', 'new_pidns', 'skip_if_env_has', 'return_code', ], ) class TestParser(object): @staticmethod def read_all(run_aot_tests): aot_tests = [] for root, subdirs, files in os.walk('./runtime'): for ignore_dir in ["engine", "scripts", "outputs"]: if ignore_dir in subdirs: subdirs.remove(ignore_dir) for filename in files: if filename.startswith("."): continue parser = TestParser.read(root + '/' + filename) if parser[1]: if run_aot_tests: for test in parser[1]: # Only reuse tests that use PROG directive if not test.prog: continue # _replace() creates a new instance w/ specified fields replaced test = test._replace( name='{}.{}'.format(test.suite, test.name), suite='aot') aot_tests.append(test) yield parser if run_aot_tests: yield ('aot', aot_tests) @staticmethod def read(file_name): tests = [] test_lines = [] test_suite = file_name.split('/')[-1] with open (file_name, 'r') as file: lines = file.readlines() line_num = 0 for line in lines: line_num += 1 if line.startswith("#"): continue if line != '\n': test_lines.append(line) if line == '\n' or line_num == len(lines): if test_lines: test_struct = TestParser.__read_test_struct(test_lines, test_suite) if not test_struct.arch or (platform.machine().lower() in test_struct.arch): tests.append(test_struct) test_lines = [] return (test_suite, tests) @staticmethod def __read_test_struct(test, test_suite): name = '' run = '' prog = '' expects = [] has_exact_expect = False timeout = '' befores = [] after = '' setup = '' cleanup = '' kernel_min = '' kernel_max = '' requirement = [] env = {} arch = [] feature_requirement = set() neg_feature_requirement = set() will_fail = False new_pidns = False skip_if_env_has = None return_code = None prev_item_name = '' for item in test: if item[:len(prev_item_name) + 1].isspace(): # Whitespace at beginning of line means it continues from the # previous line # Remove the leading whitespace and the trailing newline line = item[len(prev_item_name) + 1:-1] if prev_item_name == 'PROG': prog += '\n' + line continue elif prev_item_name in ('EXPECT', 'EXPECT_REGEX'): expects[-1].expect += '\n' + line continue item_split = item.strip().split(maxsplit=1) item_name = item_split[0] line = item_split[1] if len(item_split) > 1 else "" prev_item_name = item_name if item_name == 'NAME': name = line elif item_name == 'RUN': run = line elif item_name == "PROG": prog = line elif item_name == 'EXPECT': expects.append(Expect(line, 'text')) elif item_name == 'EXPECT_NONE': expects.append(Expect(line, 'text_none')) elif item_name == 'EXPECT_REGEX': expects.append(Expect(line, 'regex')) elif item_name == 'EXPECT_REGEX_NONE': expects.append(Expect(line, 'regex_none')) elif item_name == 'EXPECT_FILE': has_exact_expect = True expects.append(Expect(line, 'file')) elif item_name == 'EXPECT_JSON': has_exact_expect = True expects.append(Expect(line, 'json')) elif item_name == 'TIMEOUT': timeout = int(line.strip(' ')) elif item_name == 'BEFORE': befores.append(line) elif item_name == 'AFTER': after = line elif item_name == 'SETUP': setup = line elif item_name == 'CLEANUP': cleanup = line elif item_name == 'MIN_KERNEL': kernel_min = line elif item_name == 'MAX_KERNEL': kernel_max = line elif item_name == 'REQUIRES': requirement.append(line) elif item_name == 'ENV': for e in line.split(): k, v = e.split('=') env[k]=v elif item_name == 'ARCH': arch = [x.strip() for x in line.split("|")] elif item_name == 'REQUIRES_FEATURE': features = { "loop", "btf", "fentry", "probe_read_kernel", "dpath", "uprobe_refcount", "signal", "iter", "libpath_resolv", "dwarf", "kernel_dwarf", "aot", "kprobe_multi", "uprobe_multi", "skboutput", "get_tai_ns", "get_func_ip", "jiffies64", } for f in line.split(" "): f = f.strip() if f.startswith("!"): neg_feature_requirement.add(f[1:]) else: feature_requirement.add(f) 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 elif item_name == "NEW_PIDNS": new_pidns = True elif item_name == "SKIP_IF_ENV_HAS": parts = line.split("=") skip_if_env_has = (parts[0], parts[1]) elif item_name == "RETURN_CODE": return_code = int(line.strip(' ')) else: raise UnknownFieldError('Field %s is unknown. Suite: %s' % (item_name, test_suite)) if name == '': raise RequiredFieldError('Test NAME is required. Suite: ' + test_suite) elif run == '' and prog == '': raise RequiredFieldError('Test RUN or PROG is required. Suite: ' + test_suite) elif run != '' and prog != '': raise InvalidFieldError('Test RUN and PROG both specified. Suit: ' + test_suite) elif len(expects) == 0 and return_code is None: raise RequiredFieldError('At least one test EXPECT (or variation) is required. Suite: ' + test_suite) elif len(expects) > 1 and has_exact_expect: raise InvalidFieldError('EXPECT_JSON or EXPECT_FILE can not be used with other EXPECTs. Suite: ' + test_suite) elif timeout == '': timeout = DEFAULT_TIMEOUT if return_code is None: return_code = 0 return TestStruct( name, run, prog, expects, has_exact_expect, timeout, befores, after, setup, cleanup, test_suite, kernel_min, kernel_max, requirement, env, arch, feature_requirement, neg_feature_requirement, will_fail, new_pidns, skip_if_env_has, return_code, ) bpftrace-0.23.2/tests/runtime/engine/runner.py000066400000000000000000000634501477746507000213720ustar00rootroot00000000000000#!/usr/bin/python3 import json import math import subprocess import signal import sys import os import time from looseversion import LooseVersion import re from functools import lru_cache import cmake_vars BPFTRACE_BIN = os.environ["BPFTRACE_RUNTIME_TEST_EXECUTABLE"] COLOR_SETTING = os.environ.get("RUNTIME_TEST_COLOR", "auto") # This attach specific timeout doesn't make much sense - all time should just # be accounted towards TIMEOUT directive value. # # But deleting the attachment specific timeout causes hangs. And I can't quite # figure out why. The next person that reads this is encouraged to try and # debug it. But please try running the CI at least 5-10 times before merging. # The hang will cause the job to hang indefinitely, so it should be easy to # spot. ATTACH_TIMEOUT = 10 OK_COLOR = '\033[92m' WARN_COLOR = '\033[94m' ERROR_COLOR = '\033[91m' NO_COLOR = '\033[0m' def colorify(s, color): if COLOR_SETTING == "yes": use_color = True elif COLOR_SETTING == "auto": use_color = sys.stdout.isatty() elif COLOR_SETTING == "no": use_color = False else: raise ValueError("Invalid setting for RUNTIME_TEST_COLOR") return f"{color}{s}{NO_COLOR}" if use_color else s def ok(s): return colorify(s, OK_COLOR) def warn(s): return colorify(s, WARN_COLOR) def fail(s): return colorify(s, ERROR_COLOR) def to_utf8(s): return s.encode("unicode_escape").decode("utf-8") class TimeoutError(Exception): pass class Runner(object): PASS = 0 FAIL = 1 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 SKIP_NOT_IN_ALLOWLIST = 9 @staticmethod def failed(status): return status == Runner.FAIL @staticmethod def timeouted(status): return status == Runner.TIMEOUT @staticmethod def skipped(status): return status in [ Runner.SKIP_KERNEL_VERSION_MIN, Runner.SKIP_KERNEL_VERSION_MAX, Runner.SKIP_REQUIREMENT_UNSATISFIED, Runner.SKIP_ENVIRONMENT_DISABLED, Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED, Runner.SKIP_AOT_NOT_SUPPORTED, Runner.SKIP_NOT_IN_ALLOWLIST, ] @staticmethod def skip_reason(test, status): 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'" % ' && '.join(test.requirement) elif status == Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED: neg_reqs = { "!{}".format(f) for f in test.neg_feature_requirement } return "missed feature: '%s'" % ','.join( (neg_reqs | test.feature_requirement)) elif status == Runner.SKIP_ENVIRONMENT_DISABLED: return "disabled by environment variable" elif status == Runner.SKIP_AOT_NOT_SUPPORTED: return "aot does not yet support this" elif status == Runner.SKIP_NOT_IN_ALLOWLIST: return "disabled by entry not in allowlist file" else: raise ValueError("Invalid skip reason: %d" % status) @staticmethod def prepare_bpf_call(test, nsenter=[]): nsenter_prefix = (" ".join(nsenter) + " ") if len(nsenter) > 0 else "" if test.run: ret = re.sub("{{BPFTRACE}}", BPFTRACE_BIN, test.run) return nsenter_prefix + ret else: # PROG use_json = "-q -f json" if (len(test.expects) > 0 and test.expects[0].mode == "json") else "" escaped_prog = test.prog.replace("'", "'\\''") cmd = nsenter_prefix + "{} {} -e '{}'".format(BPFTRACE_BIN, use_json, escaped_prog) # We're only reusing PROG-directive tests for AOT tests if test.suite == 'aot': return cmd + " --aot /tmp/tmpprog.btaot && /tmp/tmpprog.btaot" else: return cmd @staticmethod def __handler(signum, frame): raise TimeoutError('TIMEOUT') @staticmethod @lru_cache(maxsize=1) def __get_bpffeature(): p = subprocess.Popen( [BPFTRACE_BIN, "--info"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, start_new_session=True, universal_newlines=True, ) output = p.communicate()[0] bpffeature = {} bpffeature["loop"] = output.find("Loop support: yes") != -1 bpffeature["probe_read_kernel"] = output.find("probe_read_kernel: yes") != -1 bpffeature["btf"] = output.find("btf: yes") != -1 bpffeature["fentry"] = output.find("fentry: yes") != -1 bpffeature["dpath"] = output.find("dpath: yes") != -1 bpffeature["uprobe_refcount"] = output.find("uprobe refcount") != -1 bpffeature["signal"] = output.find("send_signal: yes") != -1 bpffeature["iter"] = output.find("iter: yes") != -1 bpffeature["libpath_resolv"] = output.find("bcc library path resolution: yes") != -1 bpffeature["dwarf"] = output.find("liblldb (DWARF support): yes") != -1 bpffeature["kernel_dwarf"] = output.find("Kernel DWARF: yes") != -1 bpffeature["kprobe_multi"] = output.find("kprobe_multi: yes") != -1 bpffeature["uprobe_multi"] = output.find("uprobe_multi: yes") != -1 bpffeature["aot"] = cmake_vars.LIBBCC_BPF_CONTAINS_RUNTIME bpffeature["skboutput"] = output.find("skboutput: yes") != -1 bpffeature["get_tai_ns"] = output.find("get_ktime_ns: yes") != -1 bpffeature["get_func_ip"] = output.find("get_func_ip: yes") != -1 bpffeature["jiffies64"] = output.find("jiffies64: yes") != -1 return bpffeature @staticmethod def __wait_for_children(parent_pid, timeout, ps_format, condition): with open(os.devnull, 'w') as dn: waited=0 while waited <= timeout: run_cmd = ["ps", "--ppid", str(parent_pid), "--no-headers", "-o", ps_format] children = subprocess.run(run_cmd, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if children.returncode == 0 and children.stdout: lines = [line.decode('utf-8') for line in children.stdout.splitlines()] if (condition(lines)): return lines else: print(f"__wait_for_children error: {children.stderr}. Return code: {children.returncode}") time.sleep(0.1) waited+=0.1 return None @staticmethod def __setup_cleanup(test, setup=True): test_ident = f"{test.suite}.{test.name}" if setup: cmd = test.setup name = "SETUP" else: cmd = test.cleanup name = "CLEANUP" try: process = subprocess.run(cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, universal_newlines=True) process.check_returncode() except subprocess.CalledProcessError as e: print(fail(f"[ FAILED ] {test_ident}")) print(f"\t{name} error: %s" % to_utf8(e.stdout)) return Runner.FAIL return None @staticmethod def run_test(test): current_kernel = LooseVersion(os.uname()[2]) 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_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 if test.skip_if_env_has and os.environ.get(test.skip_if_env_has[0], '') == test.skip_if_env_has[1]: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_ENVIRONMENT_DISABLED signal.signal(signal.SIGALRM, Runner.__handler) start_time = time.time() p = None befores = [] befores_output = [] bpftrace = None after = None after_output = None cleanup = None # This is only populated if the NEW_PIDNS directive is set # and is used to enter the newly created pid namespace for all BEFOREs, # the primary RUN or PROG command, and the AFTER. nsenter = [] bpf_call = "[unknown]" failed_expects = [] def get_pid_ns_cmd(cmd): return nsenter + [os.path.abspath(x) for x in cmd.split()] def check_expect(expect, output): try: if expect.mode == "text": # Raw text match on an entire line, ignoring leading/trailing whitespace return re.search(f"^\\s*{re.escape(expect.expect)}\\s*$", output, re.M) elif expect.mode == "text_none": return not re.search(f"^\\s*{re.escape(expect.expect)}\\s*$", output, re.M) elif expect.mode == "regex": return re.search(expect.expect, output, re.M) elif expect.mode == "regex_none": return not re.search(expect.expect, output, re.M) elif expect.mode == "file": with open(expect.expect) as expect_file: # remove leading and trailing empty lines return output.strip() == expect_file.read().strip() else: with open(expect.expect) as expect_file: _, file_extension = os.path.splitext(expect.expect) stripped_output = output.strip() output_lines = stripped_output.splitlines() # ndjson files are new line delimited blocks of json # https://github.com/ndjson/ndjson-spec if file_extension == ".ndjson": stripped_file = expect_file.read().strip() file_lines = stripped_file.splitlines() if len(file_lines) != len(output_lines): return False for x in range(len(file_lines)): if json.loads(output_lines[x]) != json.loads(file_lines[x]): return False return True if len(output_lines) != 1: print(f"Expected a single line of json ouput. Got {len(output_lines)} lines") return False return json.loads(stripped_output) == json.load(expect_file) except Exception as err: print("ERROR in check_result: ", err) return False def check_result(output): all_passed = True for expect in test.expects: if not check_expect(expect, output): all_passed = False failed_expects.append(expect) return all_passed try: result = None timeout = False output = "" print(ok("[ RUN ] ") + "%s.%s" % (test.suite, test.name)) if test.requirement: for req in test.requirement: with open(os.devnull, 'w') as dn: if subprocess.call( req, shell=True, stdout=dn, stderr=dn, ) != 0: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_REQUIREMENT_UNSATISFIED if test.feature_requirement or test.neg_feature_requirement: bpffeature = Runner.__get_bpffeature() for feature in test.feature_requirement: if feature not in bpffeature: raise ValueError("Invalid feature requirement: %s" % feature) elif not bpffeature[feature]: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED for feature in test.neg_feature_requirement: if feature not in bpffeature: raise ValueError("Invalid feature requirement: %s" % feature) elif bpffeature[feature]: print(warn("[ SKIP ] ") + "%s.%s" % (test.suite, test.name)) return Runner.SKIP_FEATURE_REQUIREMENT_UNSATISFIED if test.new_pidns and not test.befores: raise ValueError("`NEW_PIDNS` requires at least one `BEFORE` directive as something needs to run in the new pid namespace") if test.setup: setup = Runner.__setup_cleanup(test, setup=True) if setup: return setup if test.befores: if test.new_pidns: # Use the first BEFORE as the first process in the new pid namespace unshare_out = subprocess.Popen(["unshare", "--fork", "--pid", "--mount-proc", "-r", "--kill-child"] + ["--"] + test.befores[0].split(), start_new_session=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) lines = Runner.__wait_for_children(unshare_out.pid, test.timeout, "pid", lambda lines : len(lines) > 0) if not lines: raise TimeoutError(f'Timed out waiting create a new PID namespace') nsenter.extend(["nsenter", "-p", "-m", "-t", lines[0].strip()]) # This is the only one we need to add to befores as killing the # unshare process will kill the whole process subtree with "--kill-child" befores.append(unshare_out) for before in test.befores[1:]: child_name = os.path.basename(before.strip().split()[0])[:15] before = subprocess.Popen(get_pid_ns_cmd(before), start_new_session=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # wait for the child of nsenter if not Runner.__wait_for_children(before.pid, test.timeout, "comm", lambda lines : child_name in lines): raise TimeoutError(f'Timed out waiting for BEFORE {before}') else: for before in test.befores: before = subprocess.Popen(before.split(), start_new_session=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) befores.append(before) child_names = [os.path.basename(x.strip().split()[-1]) for x in test.befores] child_names = sorted((x[:15] for x in child_names)) # cut to comm length def found_all_children(lines): return sorted((line.strip() for line in lines if line != 'ps')) == child_names if not Runner.__wait_for_children(os.getpid(), test.timeout, "comm", found_all_children): raise TimeoutError(f'Timed out waiting for BEFORE(s) {test.befores}') bpf_call = Runner.prepare_bpf_call(test, nsenter) if test.befores and '{{BEFORE_PID}}' in bpf_call: if test.new_pidns: # This can be fixed in the future if needed raise ValueError(f"BEFORE_PID cannot be used with NEW_PIDNS") if len(test.befores) > 1: raise ValueError(f"test has {len(test.befores)} BEFORE clauses but BEFORE_PID usage requires exactly one") child_name = test.befores[0].strip().split()[-1] child_name = os.path.basename(child_name) childpid = subprocess.Popen(["pidof", child_name], stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].split()[0] bpf_call = re.sub("{{BEFORE_PID}}", str(childpid), bpf_call) env = { 'test': test.name, '__BPFTRACE_NOTIFY_PROBES_ATTACHED': '1', '__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED': '1', 'BPFTRACE_VERIFY_LLVM_IR': '1', 'PATH': os.environ.get('PATH', ''), } env.update(test.env) p = subprocess.Popen( bpf_call, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, errors='backslashreplace', env=env, start_new_session=True, universal_newlines=True, ) bpftrace = p attached = False signal.alarm(ATTACH_TIMEOUT) while p.poll() is None: nextline = p.stdout.readline() output += nextline if not attached and nextline == "__BPFTRACE_NOTIFY_PROBES_ATTACHED\n": attached = True if test.has_exact_expect: output = "" # ignore earlier ouput signal.alarm(test.timeout) if test.after: after_cmd = get_pid_ns_cmd(test.after) if test.new_pidns else test.after after = subprocess.Popen(after_cmd, shell=True, start_new_session=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) signal.alarm(0) output += p.stdout.read() result = check_result(output) 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: if p.poll() is None: os.killpg(os.getpgid(p.pid), signal.SIGTERM) try: # SIGTERM only sets the exit flag in bpftrace event loop. # If bpftrace is stuck somewhere else, it won't exit. So we # have to set a timeout here to prevent hangs. output += p.communicate(timeout=1)[0] except subprocess.TimeoutExpired: # subprocess.communicate() docs say communicate() is only # reliable after a TimeoutExpired if you kill and retry. os.killpg(os.getpgid(p.pid), signal.SIGKILL) output += p.communicate()[0] result = check_result(output) if not result: print(fail("[ TIMEOUT ] ") + "%s.%s" % (test.suite, test.name)) print('\tCommand: %s' % bpf_call) print('\tTimeout: %s' % test.timeout) print('\tCurrent output: %s' % output) return Runner.TIMEOUT finally: if befores: for before in befores: try: befores_output.append(before.communicate(timeout=1)[0]) except subprocess.TimeoutExpired: os.killpg(os.getpgid(before.pid), signal.SIGKILL) befores_output.append(before.communicate()[0]) # Cleanup just in case if bpftrace and bpftrace.poll() is None: os.killpg(os.getpgid(p.pid), signal.SIGKILL) if after: try: after_output = after.communicate(timeout=1)[0] except subprocess.TimeoutExpired: os.killpg(os.getpgid(after.pid), signal.SIGKILL) after_output = after.communicate()[0] def print_befores_and_after_output(): if len(befores_output) > 0: for out in befores_output: print(f"\tBefore cmd output: {to_utf8(out)}") if after_output is not None: print(f"\tAfter cmd output: {to_utf8(after_output)}") if test.cleanup: cleanup = Runner.__setup_cleanup(test, setup=False) if cleanup: print('\tOutput: ' + to_utf8(output)) print_befores_and_after_output() return cleanup elapsed = math.ceil((time.time() - start_time) * 1000) label = f"{test.suite}.{test.name} ({elapsed} ms)" if '__BPFTRACE_NOTIFY_AOT_PORTABILITY_DISABLED' in to_utf8(output): print(warn("[ SKIP ] ") + label) return Runner.SKIP_AOT_NOT_SUPPORTED if p and p.returncode != test.return_code and not test.will_fail and not timeout: print(fail("[ FAILED ] ") + label) print('\tCommand: ' + bpf_call) print('\tUnclean exit code: ' + str(p.returncode)) print('\tOutput: ' + to_utf8(output)) print_befores_and_after_output() return Runner.FAIL if result: print(ok("[ OK ] ") + label) return Runner.PASS else: print(fail("[ FAILED ] ") + label) print('\tCommand: ' + bpf_call) for failed_expect in failed_expects: if failed_expect.mode == "text": print('\tExpected: ' + failed_expect.expect) print('\tFound:\n' + to_utf8(output)) elif failed_expect.mode == "text_none": print('\tExpected no: ' + failed_expect.expect) print('\tFound:\n' + to_utf8(output)) elif failed_expect.mode == "regex": print('\tExpected REGEX: ' + failed_expect.expect) print('\tFound:\n' + to_utf8(output)) elif failed_expect.mode == "regex_none": print('\tExpected no REGEX: ' + failed_expect.expect) print('\tFound:\n' + to_utf8(output)) elif failed_expect.mode == "json": _, file_extension = os.path.splitext(failed_expect.expect) # ndjson files are new line delimited blocks of json # https://github.com/ndjson/ndjson-spec if file_extension == ".ndjson": with open(failed_expect.expect) as expect_file: stripped_file = expect_file.read().strip() file_lines = stripped_file.splitlines() print('\tExpected JSON:\n') for x in file_lines: try: print(json.dumps(json.loads(x), indent=2)) except json.decoder.JSONDecodeError as err: print("Could not parse JSON: " + str(err) + '\n' + "Raw Line: " + x) stripped_output = output.strip() output_lines = stripped_output.splitlines() print('\tFound:\n') for x in output_lines: try: print(json.dumps(json.loads(x), indent=2)) except json.decoder.JSONDecodeError as err: print("Could not parse JSON: " + str(err) + '\n' + "Raw Output: " + x) else: try: expected = json.dumps(json.loads(open(failed_expect.expect).read()), indent=2) except json.decoder.JSONDecodeError as err: expected = "Could not parse JSON: " + str(err) + '\n' + "Raw File: " + expected try: found = json.dumps(json.loads(output), indent=2) except json.decoder.JSONDecodeError as err: found = "Could not parse JSON: " + str(err) + '\n' + "Raw Output: " + output print('\tExpected JSON:\n' + expected) print('\tFound:\n' + found) else: print('\tExpected FILE:\n\t\t' + to_utf8(open(failed_expect.expect).read())) print('\tFound:\n\t\t' + to_utf8(output)) print_befores_and_after_output() return Runner.FAIL bpftrace-0.23.2/tests/runtime/file-output000066400000000000000000000017001477746507000204300ustar00rootroot00000000000000NAME printf to file RUN {{BPFTRACE}} -e 'i:ms:10 { printf("%s %d\n", "SUCCESS", 1); exit() }' -o /tmp/bpftrace-file-output-test >/dev/null; cat /tmp/bpftrace-file-output-test; rm /tmp/bpftrace-file-output-test EXPECT SUCCESS 1 NAME cat to file RUN {{BPFTRACE}} -e 'i:ms:10 { cat("/proc/loadavg"); exit(); }' -o /tmp/bpftrace-file-output-test >/dev/null; cat /tmp/bpftrace-file-output-test; rm /tmp/bpftrace-file-output-test EXPECT_REGEX ^([0-9]+\.[0-9]+ )+.*$ NAME print map to file RUN {{BPFTRACE}} -e 'i:ms:10 { @=lhist(50, 0, 100, 10); exit();}' -o /tmp/bpftrace-file-output-test >/dev/null; cat /tmp/bpftrace-file-output-test; rm /tmp/bpftrace-file-output-test EXPECT_REGEX ^\[50, 60\).*\@+\|$ NAME system stdout to file RUN {{BPFTRACE}} --unsafe -e 'i:ms:10 { system("cat /proc/loadavg"); exit(); }' -o /tmp/bpftrace-file-output-test >/dev/null; cat /tmp/bpftrace-file-output-test; rm /tmp/bpftrace-file-output-test EXPECT_REGEX ^([0-9]+\.[0-9]+ )+.*$ bpftrace-0.23.2/tests/runtime/for000066400000000000000000000060711477746507000167470ustar00rootroot00000000000000NAME map one key PROG BEGIN { @map[16] = 32; @map[64] = 128; for ($kv : @map) { print($kv); } exit(); } EXPECT (16, 32) EXPECT (64, 128) NAME map two keys PROG BEGIN { @map[16,17] = 32; @map[64,65] = 128; for ($kv : @map) { print($kv); } exit(); } EXPECT ((16, 17), 32) EXPECT ((64, 65), 128) NAME map one key elements PROG BEGIN { @map[16] = 32; for ($kv : @map) { printf("key: %d, val: %d\n", $kv.0, $kv.1); } exit(); } EXPECT key: 16, val: 32 NAME map two key elements PROG BEGIN { @map[16,17] = 32; for ($kv : @map) { printf("key: (%d,%d), val: %d\n", $kv.0.0, $kv.0.1, $kv.1); } exit(); } EXPECT key: (16,17), val: 32 NAME map strings PROG BEGIN { @map["abc"] = "aa"; @map["def"] = "dd"; for ($kv : @map) { print($kv); } exit(); } EXPECT (abc, aa) EXPECT (def, dd) NAME map create map in body PROG BEGIN { @map[16] = 32; @map[64] = 128; for ($kv : @map) { @new[$kv.1] = $kv.0; } print(@new); exit(); } EXPECT @new[32]: 16 EXPECT @new[128]: 64 NAME map multiple loops PROG BEGIN { @mapA[16] = 32; @mapA[17] = 33; @mapB[64] = 128; @mapB[65] = 129; for ($kv : @mapA) { print($kv); } for ($kv : @mapB) { print($kv); } exit(); } EXPECT (16, 32) EXPECT (17, 33) EXPECT (64, 128) EXPECT (65, 129) NAME map multiple probes PROG BEGIN { @mapA[16] = 32; @mapA[17] = 33; @mapB[64] = 128; @mapB[65] = 129; for ($kv : @mapA) { print($kv); } exit(); } END { for ($kv : @mapB) { print($kv); } } EXPECT (16, 32) EXPECT (17, 33) EXPECT (64, 128) EXPECT (65, 129) NAME map nested vals PROG BEGIN { @mapA[16] = 32; @mapB[64] = 128; @mapB[65] = 129; for ($kv : @mapA) { print($kv); for ($kv2 : @mapB) { print($kv2); } } exit(); } EXPECT (16, 32) EXPECT (64, 128) EXPECT (65, 129) NAME map nested count PROG BEGIN { @mapA[16] = 32; @mapA[17] = 33; @mapB[64] = 128; @mapB[65] = 129; @mapB[66] = 130; for ($kv : @mapA) { printf("A"); for ($kv2 : @mapB) { printf("B"); } } exit(); } EXPECT ABBBABBB NAME map delete PROG BEGIN { @[0,0,0] = 1; @[0,0,1] = 1; @[0,1,0] = 1; @[1,0,0] = 1; for ($kv : @) { if ($kv.0.1 == 1) { delete(@, $kv.0); } } exit(); } EXPECT @[0, 0, 0]: 1 EXPECT @[0, 0, 1]: 1 EXPECT @[1, 0, 0]: 1 EXPECT_NONE @[0, 1, 0]: 1 NAME variable context read only PROG BEGIN { @map[0] = 0; $var = 123; for ($kv : @map) { print($var); } exit(); } EXPECT 123 NAME variable context update PROG BEGIN { @map[0] = 0; @map[1] = 1; $var = 123; for ($kv : @map) { print($var); $var *= 2; } print($var); exit(); } EXPECT_REGEX ^123\n246\n492$ NAME variable context string PROG BEGIN { @map[0] = 0; @map[1] = 1; $var = "abc"; for ($kv : @map) { print($var); $var = "def"; } print($var); exit(); } EXPECT_REGEX ^abc\ndef\ndef$ NAME variable context multiple PROG BEGIN { @map[0] = 0; $var1 = 123; $var2 = "abc"; for ($kv : @map) { print(($var1, $var2)); } exit(); } EXPECT (123, abc) NAME map two keys with a per cpu aggregation PROG BEGIN { @map[16,17] = count(); @map[16,17] = count(); @map[1,2] = count(); for ($kv : @map) { print($kv); } exit(); } EXPECT ((16, 17), 2) EXPECT ((1, 2), 1) NAME map stack key with a per cpu aggregation PROG BEGIN { @map[kstack(raw)] = count(); for ($kv : @map) { print($kv.1); } exit(); } EXPECT 1 bpftrace-0.23.2/tests/runtime/intcast000066400000000000000000000021461477746507000176250ustar00rootroot00000000000000NAME Casting retval should work PROG ur:./testprogs/uprobe_negative_retval:main /(int32)retval < 0/ { @[(int32)retval]=count(); exit();} AFTER ./testprogs/uprobe_negative_retval EXPECT @[-100]: 1 NAME Sum casted retval PROG ur:./testprogs/uprobe_negative_retval:function1 { @=stats((int32)retval); exit();} ur:./testprogs/uprobe_negative_retval:main { exit() } AFTER ./testprogs/uprobe_negative_retval EXPECT @: count 221, average -10, total -2210 NAME Casting ints PROG BEGIN{printf("Values: %d %d\n", (uint8) -10, (uint16) 131087); exit(); } EXPECT Values: 246 15 NAME Implicit truncation of ints PROG BEGIN{ $a = (int16)0; $a = 2; $b = (int8)3; $b = -100; print(($a, $b)); exit(); } EXPECT (2, -100) NAME Binop on 32bit int PROG BEGIN { $x = (uint32)5; $x += (uint32)1; print($x); exit() } EXPECT 6 NAME Binop on 16bit int PROG BEGIN { $x = (uint16)0xFF; $x &= (uint16)0xF; print($x); exit() } EXPECT 15 NAME Binop on 8bit int PROG BEGIN { $x = (uint8)0xF; $x |= (uint8)0xF0; print($x); exit() } EXPECT 255 NAME Binop promotion PROG BEGIN { $x = (uint32)0; $x = (uint32)5 + (uint16)1; print($x); exit() } EXPECT 6 bpftrace-0.23.2/tests/runtime/intptrcast000066400000000000000000000004521477746507000203510ustar00rootroot00000000000000NAME Sum casted value PROG uprobe:./testprogs/intptrcast:fn { @=sum(*((uint16*)arg0 + 1)); exit();} EXPECT @: 1110 AFTER ./testprogs/intptrcast NAME Casting ints PROG uprobe:./testprogs/intptrcast:fn { $a=*((uint16*)arg0 + 1); printf("%d\n", $a); exit();} EXPECT 1110 AFTER ./testprogs/intptrcast bpftrace-0.23.2/tests/runtime/json-output000066400000000000000000000200101477746507000204550ustar00rootroot00000000000000NAME invalid_format RUN {{BPFTRACE}} -q -f jsonx -e 'BEGIN { @scalar = 5; exit(); }' EXPECT ERROR: Invalid output format "jsonx" WILL_FAIL NAME scalar PROG BEGIN { @scalar = 5; exit(); } EXPECT_JSON runtime/outputs/scalar.json NAME scalar_str PROG BEGIN { @scalar_str = "a b \n d e"; exit(); } EXPECT_JSON runtime/outputs/scalar_str.json NAME complex PROG BEGIN { @complex[comm,2] = 5; exit(); } EXPECT_JSON runtime/outputs/complex.json NAME map PROG BEGIN { @map["key1"] = 2; @map["key2"] = 3; exit(); } EXPECT_JSON runtime/outputs/map.json NAME multiple maps PROG BEGIN { @map1["key1"] = 2; @map2["key2"] = 3; exit(); } EXPECT_JSON runtime/outputs/multiple_maps.ndjson NAME histogram PROG BEGIN { @hist = hist(2); @hist = hist(1025); exit(); } EXPECT_JSON runtime/outputs/hist.json NAME histogram zero PROG BEGIN { @hist = hist(2); zero(@hist); exit(); } EXPECT_JSON runtime/outputs/hist_zero.json NAME multiple histograms PROG BEGIN { @["bpftrace"] = hist(2); @["curl"] = hist(-1); @["curl"] = hist(0); @["curl"] = hist(511); @["curl"] = hist(1024); @["curl"] = hist(1025); exit(); } EXPECT_JSON runtime/outputs/hist_multiple.json NAME multiple histograms multiple keys PROG BEGIN { @["bpftrace", 2] = hist(2);@["curl", 3] = hist(511); @["curl", 3] = hist(1024); exit(); } EXPECT_JSON runtime/outputs/hist_multiple_multiple_keys.json NAME histogram-finegrain PROG BEGIN { $i = 0; while ($i < 1024) { @ = hist($i, 3); $i++; } exit(); } EXPECT_JSON runtime/outputs/hist_2args.json NAME linear histogram PROG BEGIN { @h = lhist(2, 0, 100, 10); @h = lhist(50, 0, 100, 10); @h = lhist(1000, 0, 100, 10); exit(); } EXPECT_JSON runtime/outputs/lhist.json NAME linear histogram zero PROG BEGIN { @h = lhist(2, 0, 100, 10); zero(@h); exit(); } EXPECT_JSON runtime/outputs/lhist_zero.json NAME multiple linear histograms PROG BEGIN { @stats["bpftrace"] = lhist(2, 0, 100, 10); @stats["curl"] = lhist(50, 0, 100, 10); @stats["bpftrace"] = lhist(1000, 0, 100, 10); exit(); } EXPECT_JSON runtime/outputs/lhist_multiple.json NAME stats PROG BEGIN { @stats = stats(2); @stats = stats(10); exit(); } EXPECT_JSON runtime/outputs/stats.json NAME multiple stats PROG BEGIN { @stats["curl"] = stats(2); @stats["zsh"] = stats(10); exit(); } EXPECT_JSON runtime/outputs/stats_multiple.json NAME printf RUN {{BPFTRACE}} -q -f json -e 'BEGIN { printf("test %d", 5); exit(); }' EXPECT {"type": "printf", "data": "test 5"} NAME printf_escaping RUN {{BPFTRACE}} -q -f json -e 'BEGIN { printf("test \r \n \t \\ \" bar"); exit(); }' EXPECT {"type": "printf", "data": "test \r \n \t \\ \" bar"} NAME time RUN {{BPFTRACE}} -q -f json -e 'BEGIN { time(); exit(); }' EXPECT_REGEX ^{"type": "time", "data": "[0-9]*:[0-9]*:[0-9]*\\n"}$ NAME syscall RUN {{BPFTRACE}} --unsafe -f json -e 'BEGIN { system("echo a b c"); exit(); }' EXPECT {"type": "syscall", "data": "a b c\n"} NAME join_delim RUN {{BPFTRACE}} --unsafe -f json -e 'tracepoint:syscalls:sys_enter_execve { join(args.argv, ","); }' -c "./testprogs/syscall execve /bin/echo 'A'" EXPECT {"type": "join", "data": "/bin/echo,'A'"} NAME cat RUN {{BPFTRACE}} -f json -e 'BEGIN { cat("/proc/uptime"); exit(); }' EXPECT_REGEX ^{"type": "cat", "data": "[0-9]*.[0-9]* [0-9]*.[0-9]*\\n"}$ NAME strerror RUN {{BPFTRACE}} -f json -e 'BEGIN { print((strerror(7))); exit(); }' EXPECT {"type": "value", "data": "Argument list too long"} # Careful with '[' and ']', they are read by the test engine as a regex # character class, so make sure to escape them. NAME tuple RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @ = (1, 2, "string", (4, 5)); exit(); }' EXPECT {"type": "map", "data": {"@": [1,2,"string",[4,5]]}} NAME tuple_with_struct RUN {{BPFTRACE}} -f json -e 'struct Foo { int m; int n; } uprobe:./testprogs/simple_struct:func { $f = *((struct Foo *) arg0); @ = (0, $f); exit(); }' EXPECT {"type": "map", "data": {"@": [0,{ "m": 2, "n": 3 }]}} AFTER ./testprogs/simple_struct NAME tuple_with_escaped_string RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @ = (1, 2, "string with \"quotes\""); exit(); }' EXPECT {"type": "map", "data": {"@": [1,2,"string with \"quotes\""]}} NAME print_non_map RUN {{BPFTRACE}} -q -f json -e 'BEGIN { $x = 5; print($x); exit() }' EXPECT {"type": "value", "data": 5} TIMEOUT 1 NAME print_non_map_builtin RUN {{BPFTRACE}} -q -f json -e 'BEGIN { print(comm); exit() }' EXPECT {"type": "value", "data": "bpftrace"} TIMEOUT 1 NAME print_non_map_tuple RUN {{BPFTRACE}} -q -f json -e 'BEGIN { $t = (1, 2, "string"); print($t); exit() }' EXPECT {"type": "value", "data": [1,2,"string"]} TIMEOUT 1 NAME print_non_map_struct RUN {{BPFTRACE}} -f json -e 'struct Foo { int m; int n; } uprobe:./testprogs/simple_struct:func { $f = *((struct Foo *) arg0); print($f); exit(); }' EXPECT {"type": "value", "data": { "m": 2, "n": 3 }} AFTER ./testprogs/simple_struct NAME print_non_map_nested_struct RUN {{BPFTRACE}} -f json -e 'struct Foo { struct { int m[1] } y; struct { int n; } a; } uprobe:./testprogs/simple_struct:func { $f = *((struct Foo *) arg0); print($f); exit(); }' EXPECT {"type": "value", "data": { "y": { "m": [2] }, "a": { "n": 3 } }} AFTER ./testprogs/simple_struct NAME print_avg_map_args RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 2, 10); clear(@); exit(); }' EXPECT {"type": "stats", "data": {"@": {"c": 3, "d": 4}}} TIMEOUT 1 NAME print_avg_map_with_large_top RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @["a"] = avg(10); @["b"] = avg(20); @["c"] = avg(30); @["d"] = avg(40); print(@, 10, 10); clear(@); exit(); }' EXPECT {"type": "stats", "data": {"@": {"a": 1, "b": 2, "c": 3, "d": 4}}} TIMEOUT 1 NAME print_hist_with_top_arg RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 2); clear(@); exit(); }' EXPECT {"type": "hist", "data": {"@": {"2": [{"min": 16, "max": 31, "count": 1}], "3": [{"min": 16, "max": 31, "count": 1}]}}} TIMEOUT 1 NAME print_hist_with_large_top_arg RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @[1] = hist(10); @[2] = hist(20); @[3] = hist(30); print(@, 10); clear(@); exit(); }' EXPECT {"type": "hist", "data": {"@": {"1": [{"min": 8, "max": 15, "count": 1}], "2": [{"min": 16, "max": 31, "count": 1}], "3": [{"min": 16, "max": 31, "count": 1}]}}} TIMEOUT 1 NAME helper_error RUN {{BPFTRACE}} -k -q -f json -e 'struct foo {int a;}; BEGIN { $tmp = ((struct foo*) 0)->a; exit(); }' EXPECT {"type": "helper_error", "msg": "Bad address", "helper": "probe_read", "retcode": -14, "line": 1, "col": 37} TIMEOUT 1 NAME cgroup_path RUN {{BPFTRACE}} -q -f json -e 'BEGIN { print(cgroup_path(cgroup)); exit(); }' | tail -n +2 | python3 -c 'import sys,json; print(json.load(sys.stdin))' EXPECT_REGEX ^{'type': 'value', 'data': '.*'}$ NAME strftime RUN {{BPFTRACE}} -q -f json -e 'BEGIN { $t = (1, strftime("%m/%d/%y", nsecs)); print($t); exit() }' | tail -n +2 | python3 -c 'import sys,json; print(json.load(sys.stdin))' EXPECT_REGEX ^{'type': 'value', 'data': \[1, '[0-9]{2}\/[0-9]{2}\/[0-9]{2}'\]}$ TIMEOUT 1 NAME print_hex_values RUN {{BPFTRACE}} -q -f json -e 'BEGIN { @=(int16*) 0x32; exit(); }' EXPECT {"type": "map", "data": {"@": 50}} # To preserve backwards compat for machines parsing output, we do not symbolize enums in JSON output mode NAME enum_not_symbolized RUN {{BPFTRACE}} -q -f json -e 'enum { FOO = 333 }; BEGIN { $f = FOO; print($f); exit(); }' EXPECT {"type": "value", "data": 333} # But if user explicitly asks for enum symbolization, we provide it in JSON output NAME enum_symbolized_printf RUN {{BPFTRACE}} -q -f json -e 'enum Foo { ONE = 1, TWO = 2, OTHER = 99999 }; BEGIN { printf("%d %s %d %s %d %s\n", ONE, ONE, TWO, TWO, OTHER, OTHER); exit() }' EXPECT {"type": "printf", "data": "1 ONE 2 TWO 99999 OTHER\n"} # Note: if we fix the underlying issue, which is that we don't support unions # or anon structs, then we will need to change/delete this test NAME none type RUN {{BPFTRACE}} -q -f json -e 'union N { int i; float f; }; struct Foo { int m; union N n; }; uprobe:./testprogs/struct_with_union:func { print(*((struct Foo *) arg0)); exit(); }' EXPECT {"type": "value", "data": { "m": 2, "n": { "i": 5, "f": "" } }} AFTER ./testprogs/struct_with_union bpftrace-0.23.2/tests/runtime/languages000066400000000000000000000014271477746507000201270ustar00rootroot00000000000000# This only tests for the presence of mangled rust probe names. We need to add # support for automatically unmangling the names (and include the 'rust' # language type). NAME uprobes - list rust uprobes (mangled) REQUIRES testprogs/hello_rust RUN {{BPFTRACE}} -l 'uprobe:./testprogs/hello_rust:*' EXPECT_REGEX uprobe:./testprogs/hello_rust.*fun1 # This only tests for the presence of the builtin symbol `runtime.schedule`. # Argument unpacking and other things will *not* be supported in Go for a # while, since the calling convention is different. But useful analysis can # still be done with the basics. NAME uprobes - list Go uprobes (no arguments) REQUIRES testprogs/hello_go RUN {{BPFTRACE}} -l 'uprobe:./testprogs/hello_go:*' EXPECT_REGEX uprobe:./testprogs/hello_go:runtime.schedule bpftrace-0.23.2/tests/runtime/other000066400000000000000000000264371477746507000173120ustar00rootroot00000000000000NAME if_gt PROG i:ms:1 {$a = 10; if ($a > 2) { $a = 20 } printf("a=%d\n", $a); exit();} EXPECT a=20 NAME if_lt PROG i:ms:1 {$a = 10; if ($a < 2) { $a = 20 } printf("a=%d\n", $a); exit();} EXPECT a=10 NAME ifelse_go_else PROG i:ms:1 {$a = ""; if (10 < 2) { $a = "hi" } else {$a = "hello"} printf("a=%s\n", $a); exit();} EXPECT a=hello NAME ifelse_go_if PROG i:ms:1 {$a = ""; if (10 > 2) { $a = "hi" } else {$a = "hello"} printf("a=%s\n", $a); exit();} EXPECT a=hi NAME ifelseif_go_elseif PROG i:ms:1 {$a = ""; if (1 > 2) { $a = "hi" } else if (2 < 3) { $a = "hello" } printf("a=%s\n", $a); exit();} EXPECT a=hello NAME ifelseifelse_go_else PROG i:ms:1 {$a = ""; if (1 > 2) { $a = "hi" } else if (5 < 3) { $a = "hello" } else { $a = "asdf" } printf("a=%s\n", $a); exit();} EXPECT a=asdf NAME if_cast PROG i:ms:1 { if ((int32)pid) {} printf("done\n"); exit();} EXPECT done NAME ternary PROG i:ms:1 { $a = 1 ? "yes" : "no"; printf("%s\n", $a); exit();} EXPECT yes NAME ternary_lnot PROG i:ms:1 { $a = !nsecs ? 0 : 1; printf("%d\n", $a); exit() } EXPECT 1 NAME ternary_int8 PROG i:ms:1 { $a = 1 ? (int8)1 : 0; printf("%d\n", $a); exit();} EXPECT 1 NAME ternary_none_type PROG i:ms:1 { nsecs ? printf("yes\n") : printf("no") ; exit(); } EXPECT yes NAME ternary_buf PROG i:ms:1 { $a = nsecs ? buf("hi", 2) : buf("bye", 3); print($a); exit(); } EXPECT hi NAME ternary_tuple PROG i:ms:1 { $a = nsecs ? ("hellolongstr", "a") : ("b", "hellolongstr"); print($a); exit(); } EXPECT (hellolongstr, a) NAME unroll PROG i:ms:1 {$a = 1; unroll (10) { $a = $a + 1; } printf("a=%d\n", $a); exit();} EXPECT a=11 NAME unroll_max_value PROG i:ms:1 {$a = 1; unroll (101) { $a = $a + 2; } printf("a=%d\n", $a); exit();} EXPECT stdin:1:17-29: ERROR: unroll maximum value is 100 i:ms:1 {$a = 1; unroll (101) { $a = $a + 2; } printf("a=%d\n", $a); exit();} ~~~~~~~~~~~~ WILL_FAIL NAME unroll_min_value PROG i:ms:1 {$a = 1; unroll (0) { $a = $a + 2; } printf("a=%d\n", $a); exit();} EXPECT stdin:1:17-27: ERROR: unroll minimum value is 1 i:ms:1 {$a = 1; unroll (0) { $a = $a + 2; } printf("a=%d\n", $a); exit();} ~~~~~~~~~~ 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 EXPECT a=11 NAME unroll_printf PROG BEGIN { unroll (1) { printf("a"); } printf("b\n"); exit(); } EXPECT ab NAME if_compare_and_print_string PROG BEGIN { if (comm == "bpftrace") { printf("comm: %s\n", comm);} exit();} EXPECT comm: bpftrace NAME struct positional string compare - equal returns true RUN {{BPFTRACE}} -e 'BEGIN { if (str($1) == str($2)) { printf("I got %s\n", str($1));} exit();}' "hello" "hello" EXPECT I got hello NAME struct positional string compare - equal returns false RUN {{BPFTRACE}} -e 'BEGIN { if (str($1) == str($2)) { printf("I got %s\n", str($1));} else { printf("not equal\n");} exit();}' "hi" "hello" EXPECT not equal NAME struct positional string compare - not equal RUN {{BPFTRACE}} -e 'BEGIN { if (str($1) != str($2)) { printf("I got %s\n", str($1));} else { printf("not equal\n");} exit();}' "hello" "hello" EXPECT not equal NAME positional string compare via variable - equal RUN {{BPFTRACE}} -e 'BEGIN { $x = str($1, 6); if ($x == "hello") { printf("I got %s\n", "hello");} else { printf("not equal\n");} exit();}' "hello" EXPECT I got hello NAME positional string compare via variable - not equal RUN {{BPFTRACE}} -e 'BEGIN { $x = str($1, 5); if ($x == "hello") { printf("I got hello\n");} else { printf("not %s\n", "equal");} exit();}' "hell" EXPECT not equal NAME positional attachpoint RUN {{BPFTRACE}} -e 'i:ms:$1 { printf("hello world\n"); exit(); }' 1 EXPECT hello world TIMEOUT 1 NAME positional attachpoint probe RUN {{BPFTRACE}} -e 'BEG$1 { printf("hello world\n"); exit(); }' IN EXPECT hello world TIMEOUT 1 NAME positional attachpoint full RUN {{BPFTRACE}} -e '$1 { printf("hello world\n"); exit(); }' i:ms:1 EXPECT hello world TIMEOUT 1 NAME positional unsigned hex RUN {{BPFTRACE}} -e 'BEGIN {printf("%lu", $1);}' 0xffffffffffffffff EXPECT 18446744073709551615 TIMEOUT 1 NAME string compare map lookup RUN {{BPFTRACE}} -e 't:syscalls:sys_enter_openat /comm == "syscall"/ { @[comm] = 1; }' -c "./testprogs/syscall openat" EXPECT @[syscall]: 1 NAME struct partial string compare - pass RUN {{BPFTRACE}} -e 'BEGIN { if (strncmp(str($1), str($2), 4) == 0) { printf("I got %s\n", str($1));} exit();}' "hhvm" "hhvm-proc" EXPECT I got hhvm NAME struct partial string compare - pass reverse RUN {{BPFTRACE}} -e 'BEGIN { if (strncmp(str($1), str($2), 4) == 0) { printf("I got %s\n", str($1));} exit();}' "hhvm-proc" "hhvm" EXPECT I got hhvm-proc NAME strncmp function argument RUN {{BPFTRACE}} -e 'struct F {char s[8];} u:./testprogs/string_args:print { @=strncmp(((struct F*)arg0)->s, "hello", 5); }' -c ./testprogs/string_args EXPECT @: 0 NAME short non null-terminated string print RUN {{BPFTRACE}} -e 'struct F {char s[5];} u:./testprogs/string_args:print { $a = ((struct F*)arg0)->s; printf("%s %s\n", $a, $a); }' -c ./testprogs/string_args EXPECT hello hello NAME optional_positional_int PROG BEGIN { printf("-%d-\n", $1); exit() } EXPECT -0- TIMEOUT 1 NAME optional_positional_str PROG BEGIN { printf("-%s-\n", str($1)); exit() } EXPECT -- TIMEOUT 1 NAME positional number as string RUN {{BPFTRACE}} -e 'BEGIN { printf("-%s-\n", str($1)); exit() }' 1 EXPECT -1- TIMEOUT 1 NAME positional as string literal RUN {{BPFTRACE}} -e 'BEGIN { @ = kaddr(str($1)); exit() }' vfs_read EXPECT Attaching 1 probe... NAME positional arg count RUN {{BPFTRACE}} -e 'BEGIN { printf("got %d args: %s %d\n", $#, str($1), $2); exit();}' "one" 2 EXPECT got 2 args: one 2 NAME positional multiple bases RUN {{BPFTRACE}} -e 'BEGIN { printf("got: %d %o 0x%x\n", $1, $2, $3); exit() }' 123 0775 0x123 EXPECT got: 123 775 0x123 NAME positional pointer arithmetic RUN {{BPFTRACE}} -e 'BEGIN { printf("%s", str($1 + 1)); exit(); }' hello EXPECT ello TIMEOUT 1 NAME positional strncmp RUN {{BPFTRACE}} -e 'BEGIN { if (strncmp("hello", "hell", $1) == 0) { printf("ok\n"); } exit(); }' 3 EXPECT ok TIMEOUT 1 NAME positional lhist RUN {{BPFTRACE}} -e 'BEGIN { @ = lhist(0, $1, $2, $3); exit()}' 0 10000 1000 EXPECT_REGEX @: *\n[\[(].* NAME positional buf RUN {{BPFTRACE}} -e 'BEGIN { @ = buf("hello", $1); exit(); }' 5 EXPECT @: hello TIMEOUT 1 NAME positional kstack RUN {{BPFTRACE}} -e 'k:do_nanosleep { printf("SUCCESS %s\n%s\n", kstack(), kstack($1)); exit(); }' 1 EXPECT SUCCESS AFTER ./testprogs/syscall nanosleep 1e8 NAME positional ustack RUN {{BPFTRACE}} -e 'u:./testprogs/uprobe_loop:uprobeFunction1 { printf("%s\n%s\n", ustack(), ustack($1)); exit(); }' 1 EXPECT_REGEX .*uprobeFunction1\+[0-9]+\n\s+spin\+[0-9]+\n\s+main\+[0-9]+\n(?s:.*)\n\n\n\s+uprobeFunction1\+[0-9]+ # Required to skip function prologue REQUIRES_FEATURE dwarf AFTER ./testprogs/uprobe_loop NAME lhist can be cleared PROG BEGIN{ @[1] = lhist(3,0,10,1); clear(@); exit() } EXPECT_REGEX .* TIMEOUT 1 NAME hist can be cleared PROG BEGIN{ @[1] = hist(1); clear(@); exit() } EXPECT_REGEX .* TIMEOUT 1 NAME stats can be cleared PROG BEGIN{ @[1] = stats(1); clear(@); exit() } EXPECT_REGEX .* TIMEOUT 1 NAME avg can be cleared PROG BEGIN{ @[1] = avg(1); clear(@); exit() } EXPECT_REGEX .* TIMEOUT 1 NAME sigint under heavy load RUN {{BPFTRACE}} --unsafe -e 'tracepoint:sched:sched_switch { system("echo foo"); } END { printf("end"); }' EXPECT end AFTER ./testprogs/syscall nanosleep 2e9; pkill -SIGINT bpftrace NAME bitfield access PROG struct Foo { unsigned int a:4, b:8, c:3, d:1, e:16; } uprobe:./testprogs/bitfield_test:func{ $foo = (struct Foo *)arg0; printf("%d %d %d %d %d\n", $foo->a, $foo->b, $foo->c, $foo->d, $foo->e); exit()} EXPECT 1 2 5 0 65535 AFTER ./testprogs/bitfield_test NAME bitfield_access_2 PROG struct Bar { short a:4, b:8, c:3, d:1; int e:9, f:15, g:1, h:2, i:5 } uprobe:./testprogs/bitfield_test:func2 { $bar = (struct Bar *)arg0; printf("%d %d %d %d %d", $bar->a, $bar->b, $bar->c, $bar->d, $bar->e); printf(" %d %d %d %d", $bar->f, $bar->g, $bar->h, $bar->i); exit()} EXPECT 1 217 5 1 500 31117 1 2 27 AFTER ./testprogs/bitfield_test NAME exit exits immediately PROG i:ms:100 { @++; exit(); @++ } EXPECT @: 1 TIMEOUT 1 NAME map_assign_map_ptr PROG i:ms:100 { @ = curtask; @a = @; printf("%p\n", @a); exit(); } EXPECT_REGEX 0x[0-9a-f]+ TIMEOUT 1 NAME runtime_error_check_delete RUN {{BPFTRACE}} -k -e 'i:ms:100 { @[1] = 1; delete(@, 2); exit(); }' EXPECT stdin:1:22-34: WARNING: Can't delete map element because it does not exist. Additional Info - helper: map_delete_elem, retcode: -2 i:ms:100 { @[1] = 1; delete(@, 2); exit(); } ~~~~~~~~~~~~ TIMEOUT 1 NAME runtime_error_check_lookup RUN {{BPFTRACE}} -k -e 'i:ms:100 { @[1] = 1; printf("%d\n", @[2]); exit(); }' EXPECT stdin:1:37-41: WARNING: Can't lookup map element because it does not exist. Additional Info - helper: map_lookup_elem, retcode: 0 i:ms:100 { @[1] = 1; printf("%d\n", @[2]); exit(); } ~~~~ TIMEOUT 1 NAME per_cpu_map_count_if PROG i:ms:1 { @ = count(); if (@ > 5) { printf("done\n"); exit(); }} EXPECT done NAME per_cpu_map_min_if PROG BEGIN { @ = 10; } i:ms:1 { @--; @mn = min(@); if (@mn < 5) { printf("done\n"); exit(); }} EXPECT done NAME per_cpu_map_max_if PROG BEGIN { @ = 1; } i:ms:1 { @++; @mx = max(@); if (@mx > 5) { printf("done\n"); exit(); }} EXPECT done NAME per_cpu_map_avg_if PROG BEGIN { @ = avg(1); @ = avg(1); @ = avg(10); if (@ == 4) { printf("done\n"); exit(); }} EXPECT done NAME per_cpu_map_arithmetic PROG BEGIN { @a = sum(10); @b = count(); $c = @a + @b; if ($c == 11) { printf("done\n"); } exit();} EXPECT done NAME per_cpu_map_cast PROG BEGIN { @a = count(); @b = sum(10); printf("%d-%d\n", (uint64)@a, (int64)@b); exit();} EXPECT 1-10 NAME per_cpu_map_as_map_key PROG BEGIN {@a = count(); @b = sum(5); @c = min(1); @d = max(10); @e = avg(7); @[@a, @b, @c, @d, @e] = 1; exit(); } EXPECT @[1, 5, 1, 10, 7]: 1 NAME dry run empty output RUN {{BPFTRACE}} --dry-run -e 'BEGIN { printf("hello\n"); @ = 0; }' EXPECT_NONE hello EXPECT_NONE @: 0 NAME symbolize enum in map key PROG enum { ONE = 1, TWO = 2 }; BEGIN { @[ONE] = 11; @[TWO] = 22; @m[ONE] = 333; exit(); } EXPECT @[ONE]: 11 EXPECT @[TWO]: 22 EXPECT @m[ONE]: 333 NAME symbolize enum in tuple map key PROG enum { ONE = 1, TWO = 2 }; BEGIN { @[ONE,TWO,ONE] = -1; exit(); } EXPECT @[ONE, TWO, ONE]: -1 NAME symbolize enum in map value PROG enum { ONE = 1, TWO = 2 }; BEGIN { @ = ONE; exit(); } EXPECT @: ONE NAME symbolize enum in tuple map value PROG enum { ONE = 1, TWO = 2 }; BEGIN { @ = (ONE, TWO); exit(); } EXPECT @: (ONE, TWO) NAME no symbolize enum after arithmetic PROG enum { ONE = 1, TWO = 2 }; BEGIN { @[ONE+1] = TWO-1; exit(); } EXPECT @[2]: 1 NAME no symbolize enum after arithmetic mixed PROG enum { ONE = 1, TWO = 2 }; BEGIN { @[ONE+1] = TWO-1; @[ONE] = ONE; exit(); } EXPECT @[2]: 1 EXPECT @[1]: 1 NAME symbolize enum in scratch variable PROG enum { FOO = 333 }; BEGIN { $f = FOO; print($f); exit(); } EXPECT FOO NAME symbolize enum in scratch variable tuple PROG enum { FOO = 333, BAR }; BEGIN { $t = (FOO, BAR); print($t); exit(); } EXPECT (FOO, BAR) NAME symbolize int cast to enum PROG enum BAZ { FOO = 333, BAR }; BEGIN { print((enum BAZ)333); exit(); } EXPECT FOO NAME no symbolize int cast to unknown enum variant PROG enum BAZ { FOO = 333, BAR }; BEGIN { $x = 444; print((enum BAZ)$x); exit(); } EXPECT 444 bpftrace-0.23.2/tests/runtime/outputs/000077500000000000000000000000001477746507000177555ustar00rootroot00000000000000bpftrace-0.23.2/tests/runtime/outputs/complex.json000066400000000000000000000001021477746507000223100ustar00rootroot00000000000000{"type": "map", "data": { "@complex": { "bpftrace,2": 5} }} bpftrace-0.23.2/tests/runtime/outputs/hist.json000066400000000000000000000007331477746507000216220ustar00rootroot00000000000000{"type": "hist", "data": { "@hist": [ {"min": 2, "max": 3, "count": 1}, {"min": 4, "max": 7, "count": 0}, {"min": 8, "max": 15, "count": 0}, {"min": 16, "max": 31, "count": 0}, {"min": 32, "max": 63, "count": 0}, {"min": 64, "max": 127, "count": 0}, {"min": 128, "max": 255, "count": 0}, {"min": 256, "max": 511, "count": 0}, {"min": 512, "max": 1023, "count": 0}, {"min": 1024, "max": 2047, "count": 1} ] }} bpftrace-0.23.2/tests/runtime/outputs/hist.txt000066400000000000000000000010641477746507000214660ustar00rootroot00000000000000@: (..., 0) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [0] 0 | | [1] 0 | | [2, 4) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [4, 8) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [8, 16) 0 | | [16, 32) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | bpftrace-0.23.2/tests/runtime/outputs/hist_10g.json000066400000000000000000000002211477746507000222610ustar00rootroot00000000000000{ "type": "hist", "data": { "@": [ { "min": 8589934592, "max": 17179869183, "count": 1 } ] } } bpftrace-0.23.2/tests/runtime/outputs/hist_2_values.txt000066400000000000000000000005041477746507000232640ustar00rootroot00000000000000@: [2, 4) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [4, 8) 0 | | [8, 16) 0 | | [16, 32) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| bpftrace-0.23.2/tests/runtime/outputs/hist_2args.json000066400000000000000000000045361477746507000227250ustar00rootroot00000000000000{"type": "hist", "data": {"@": [{"min": 0, "max": 0, "count": 1}, {"min": 1, "max": 1, "count": 1}, {"min": 2, "max": 2, "count": 1}, {"min": 3, "max": 3, "count": 1}, {"min": 4, "max": 4, "count": 1}, {"min": 5, "max": 5, "count": 1}, {"min": 6, "max": 6, "count": 1}, {"min": 7, "max": 7, "count": 1}, {"min": 8, "max": 8, "count": 1}, {"min": 9, "max": 9, "count": 1}, {"min": 10, "max": 10, "count": 1}, {"min": 11, "max": 11, "count": 1}, {"min": 12, "max": 12, "count": 1}, {"min": 13, "max": 13, "count": 1}, {"min": 14, "max": 14, "count": 1}, {"min": 15, "max": 15, "count": 1}, {"min": 16, "max": 17, "count": 2}, {"min": 18, "max": 19, "count": 2}, {"min": 20, "max": 21, "count": 2}, {"min": 22, "max": 23, "count": 2}, {"min": 24, "max": 25, "count": 2}, {"min": 26, "max": 27, "count": 2}, {"min": 28, "max": 29, "count": 2}, {"min": 30, "max": 31, "count": 2}, {"min": 32, "max": 35, "count": 4}, {"min": 36, "max": 39, "count": 4}, {"min": 40, "max": 43, "count": 4}, {"min": 44, "max": 47, "count": 4}, {"min": 48, "max": 51, "count": 4}, {"min": 52, "max": 55, "count": 4}, {"min": 56, "max": 59, "count": 4}, {"min": 60, "max": 63, "count": 4}, {"min": 64, "max": 71, "count": 8}, {"min": 72, "max": 79, "count": 8}, {"min": 80, "max": 87, "count": 8}, {"min": 88, "max": 95, "count": 8}, {"min": 96, "max": 103, "count": 8}, {"min": 104, "max": 111, "count": 8}, {"min": 112, "max": 119, "count": 8}, {"min": 120, "max": 127, "count": 8}, {"min": 128, "max": 143, "count": 16}, {"min": 144, "max": 159, "count": 16}, {"min": 160, "max": 175, "count": 16}, {"min": 176, "max": 191, "count": 16}, {"min": 192, "max": 207, "count": 16}, {"min": 208, "max": 223, "count": 16}, {"min": 224, "max": 239, "count": 16}, {"min": 240, "max": 255, "count": 16}, {"min": 256, "max": 287, "count": 32}, {"min": 288, "max": 319, "count": 32}, {"min": 320, "max": 351, "count": 32}, {"min": 352, "max": 383, "count": 32}, {"min": 384, "max": 415, "count": 32}, {"min": 416, "max": 447, "count": 32}, {"min": 448, "max": 479, "count": 32}, {"min": 480, "max": 511, "count": 32}, {"min": 512, "max": 575, "count": 64}, {"min": 576, "max": 639, "count": 64}, {"min": 640, "max": 703, "count": 64}, {"min": 704, "max": 767, "count": 64}, {"min": 768, "max": 831, "count": 64}, {"min": 832, "max": 895, "count": 64}, {"min": 896, "max": 959, "count": 64}, {"min": 960, "max": 1023, "count": 64}]}} bpftrace-0.23.2/tests/runtime/outputs/hist_multiple.json000066400000000000000000000012211477746507000235260ustar00rootroot00000000000000{"type": "hist", "data": { "@": { "bpftrace": [ {"min": 2, "max": 3, "count": 1} ], "curl": [ {"max": -1, "count": 1}, {"min": 0, "max": 0, "count": 1}, {"min": 1, "max": 1, "count": 0}, {"min": 2, "max": 3, "count": 0}, {"min": 4, "max": 7, "count": 0}, {"min": 8, "max": 15, "count": 0}, {"min": 16, "max": 31, "count": 0}, {"min": 32, "max": 63, "count": 0}, {"min": 64, "max": 127, "count": 0}, {"min": 128, "max": 255, "count": 0}, {"min": 256, "max": 511, "count": 1}, {"min": 512, "max": 1023, "count": 0}, {"min": 1024, "max": 2047, "count": 2} ]} }} bpftrace-0.23.2/tests/runtime/outputs/hist_multiple_multiple_keys.json000066400000000000000000000004751477746507000265060ustar00rootroot00000000000000{ "type": "hist", "data": { "@": { "bpftrace,2": [ { "min": 2, "max": 3, "count": 1 } ], "curl,3": [ { "min": 256, "max": 511, "count": 1 }, { "min": 512, "max": 1023, "count": 0 }, { "min": 1024, "max": 2047, "count": 1 } ] } } }bpftrace-0.23.2/tests/runtime/outputs/hist_zero.json000066400000000000000000000000541477746507000226550ustar00rootroot00000000000000{"type": "hist", "data": { "@hist": [] }} bpftrace-0.23.2/tests/runtime/outputs/lhist.json000066400000000000000000000007571477746507000220040ustar00rootroot00000000000000{"type": "hist", "data": { "@h": [ {"min": 0, "max": 9, "count": 1}, {"min": 10, "max": 19, "count": 0}, {"min": 20, "max": 29, "count": 0}, {"min": 30, "max": 39, "count": 0}, {"min": 40, "max": 49, "count": 0}, {"min": 50, "max": 59, "count": 1}, {"min": 60, "max": 69, "count": 0}, {"min": 70, "max": 79, "count": 0}, {"min": 80, "max": 89, "count": 0}, {"min": 90, "max": 99, "count": 0}, {"min": 100, "count": 1} ] }} bpftrace-0.23.2/tests/runtime/outputs/lhist.txt000066400000000000000000000010641477746507000216420ustar00rootroot00000000000000@: (..., 0) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [0, 2) 0 | | [2, 4) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [4, 6) 0 | | [6, 8) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [8, 10) 0 | | [10, ...) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | bpftrace-0.23.2/tests/runtime/outputs/lhist_multiple.json000066400000000000000000000011001477746507000236760ustar00rootroot00000000000000{"type": "hist", "data": { "@stats": { "curl": [ {"min": 50, "max": 59, "count": 1} ], "bpftrace": [ {"min": 0, "max": 9, "count": 1}, {"min": 10, "max": 19, "count": 0}, {"min": 20, "max": 29, "count": 0}, {"min": 30, "max": 39, "count": 0}, {"min": 40, "max": 49, "count": 0}, {"min": 50, "max": 59, "count": 0}, {"min": 60, "max": 69, "count": 0}, {"min": 70, "max": 79, "count": 0}, {"min": 80, "max": 89, "count": 0}, {"min": 90, "max": 99, "count": 0}, {"min": 100, "count": 1} ]} }} bpftrace-0.23.2/tests/runtime/outputs/lhist_zero.json000066400000000000000000000000511477746507000230260ustar00rootroot00000000000000{"type": "hist", "data": { "@h": [] }} bpftrace-0.23.2/tests/runtime/outputs/map.json000066400000000000000000000001071477746507000214230ustar00rootroot00000000000000{"type": "map", "data": { "@map": { "key1": 2, "key2": 3} }} bpftrace-0.23.2/tests/runtime/outputs/multiple_maps.ndjson000066400000000000000000000001461477746507000240460ustar00rootroot00000000000000{"type": "map", "data": { "@map1": { "key1": 2} }} {"type": "map", "data": { "@map2": { "key2": 3} }} bpftrace-0.23.2/tests/runtime/outputs/print_avg_map_args.txt000066400000000000000000000000211477746507000243510ustar00rootroot00000000000000@[c]: 3 @[d]: 4 bpftrace-0.23.2/tests/runtime/outputs/print_avg_map_with_large_top.txt000066400000000000000000000000411477746507000264260ustar00rootroot00000000000000@[a]: 1 @[b]: 2 @[c]: 3 @[d]: 4 bpftrace-0.23.2/tests/runtime/outputs/scalar.json000066400000000000000000000000541477746507000221140ustar00rootroot00000000000000{"type": "map", "data": { "@scalar": 5 }} bpftrace-0.23.2/tests/runtime/outputs/scalar_str.json000066400000000000000000000000731477746507000230050ustar00rootroot00000000000000{"type": "map", "data": { "@scalar_str": "a b \n d e" }} bpftrace-0.23.2/tests/runtime/outputs/stats.json000066400000000000000000000001231477746507000220020ustar00rootroot00000000000000{"type": "stats", "data": { "@stats": {"count": 2, "average": 6, "total": 12} }} bpftrace-0.23.2/tests/runtime/outputs/stats_multiple.json000066400000000000000000000002261477746507000237210ustar00rootroot00000000000000{"type": "stats", "data": { "@stats": { "curl": {"count": 1, "average": 2, "total": 2}, "zsh": {"count": 1, "average": 10, "total": 10}} }} bpftrace-0.23.2/tests/runtime/pid_namespace000066400000000000000000000035741477746507000207560ustar00rootroot00000000000000# Run bpftrace inside a PID namespace and get its own PID/TID # They should both be "1" as bpftrace is the first and only process in this # temporary namespace. NAME pid_tid_inside RUN unshare --fork --pid --mount-proc {{BPFTRACE}} -e 'BEGIN { printf("pid: %d, tid: %d\n", pid, tid); exit() }' EXPECT pid: 1, tid: 1 # Run target program inside a PID namespace and bpftrace outside # We should see PID/TID != 1 as we want to view them from the init-namespace, # where bpftrace is, not from inside the target's namespace. NAME pid_tid_outside RUN {{BPFTRACE}} -e 'uprobe:./testprogs/uprobe_loop:uprobeFunction1 { printf("pid: %d, tid: %d\n", pid, tid); exit(); }' AFTER unshare --fork --pid --mount-proc ./testprogs/uprobe_loop EXPECT_REGEX ^pid: \d+, tid: \d+$ EXPECT_REGEX_NONE ^pid: 1, tid: 1$ # Both bpftrace and target running inside a PID namespace NAME ustack_inside RUN unshare --fork --pid --mount-proc bash -c "(./testprogs/uprobe_loop &) && {{BPFTRACE}} -e 'uprobe:./testprogs/uprobe_loop:uprobeFunction1 { print(ustack); exit(); }'" EXPECT_REGEX uprobeFunction1\+\d+$ spin\+\d+$ main\+\d+$ # The stack output expects prologue to be skipped on uprobes. See kernel: # cfa7f3d2c526 ("perf,x86: avoid missing caller address in stack traces captured in uprobe") MIN_KERNEL 6.12 # Target inside a PID namespace, bpftrace outside # This test assumes we're starting in the init-namespace, which # might not be true (e.g. on WSL2), so we skip it if that's not # the case. NAME ustack_outside RUN {{BPFTRACE}} -e 'uprobe:./testprogs/uprobe_loop:uprobeFunction1 { print(ustack); exit(); }' AFTER unshare --fork --pid --mount-proc bash -c ./testprogs/uprobe_loop EXPECT_REGEX uprobeFunction1\+\d+$ spin\+\d+$ main\+\d+$ # See above MIN_KERNEL 6.12 REQUIRES [ "$(stat -Lc %i /proc/1/ns/pid)" -eq "$((0xeffffffc))" ] bpftrace-0.23.2/tests/runtime/pointers000066400000000000000000000053251477746507000200250ustar00rootroot00000000000000NAME u8 pointer increment PROG BEGIN { @=(int8*) 0x32; @+=1; exit(); } EXPECT @: 0x33 NAME u16 pointer increment PROG BEGIN { @=(int16*) 0x32; @+=1; exit(); } EXPECT @: 0x34 NAME u32 pointer increment PROG BEGIN { @=(int32*) 0x32; @+=1; exit(); } EXPECT @: 0x36 NAME u64 pointer increment PROG BEGIN { @=(int64*) 0x32; @+=1; exit(); } EXPECT @: 0x3a NAME u8 pointer unop post increment PROG BEGIN { @=(int8*) 0x32; @++; exit(); } EXPECT @: 0x33 NAME u16 pointer unop post increment PROG BEGIN { @=(int16*) 0x32; @++; exit(); } EXPECT @: 0x34 NAME u32 pointer unop post increment PROG BEGIN { @=(int32*) 0x32; @++; exit(); } EXPECT @: 0x36 NAME u64 pointer unop post increment PROG BEGIN { @=(int64*) 0x32; @++; exit(); } EXPECT @: 0x3a NAME u8 pointer unop pre increment PROG BEGIN { @=(int8*) 0x32; @++; exit(); } EXPECT @: 0x33 NAME u16 pointer unop pre increment PROG BEGIN { @=(int16*) 0x32; @++; exit(); } EXPECT @: 0x34 NAME u32 pointer unop pre increment PROG BEGIN { @=(int32*) 0x32; @++; exit(); } EXPECT @: 0x36 NAME u64 pointer unop pre increment PROG BEGIN { @=(int64*) 0x32; @++; exit(); } EXPECT @: 0x3a NAME Pointer decrement 1 PROG BEGIN { @=(int32*) 0x32; @-=1; exit(); } EXPECT @: 0x2e NAME Pointer decrement PROG BEGIN { @=(int32*) 0x32; @--; exit(); } EXPECT @: 0x2e NAME Pointer increment 6 PROG BEGIN { @=(int32*) 0x32; @+=6; exit(); } EXPECT @: 0x4a NAME Pointer decrement 6 PROG BEGIN { @=(int32*) 0x32; @-=6; exit(); } EXPECT @: 0x1a NAME Struct pointer math PROG struct A {int32_t a; int32_t b; char c[28] }; BEGIN { $a=(struct A*) 1024; printf("%lu - 5 x %lu = %lu\n", $a, sizeof(struct A), $a-5); exit(); } EXPECT 1024 - 5 x 36 = 844 NAME Pointer decrement with map PROG BEGIN { @dec = 4; @=(int32*) 0x32; @-=@dec; exit(); } EXPECT @: 0x22 NAME Pointer walk through struct RUN {{BPFTRACE}} runtime/scripts/struct_walk.bt -c ./testprogs/struct_walk EXPECT a: 45 b: 1000 NAME Pointer arith with int32 PROG struct C { uint32_t a; }; uprobe:./testprogs/struct_walk:clear { $c = (struct C *)arg0; printf("ptr: %p\n", $c + $c->a); exit() } AFTER ./testprogs/struct_walk EXPECT_REGEX ^ptr: 0x[0-9a-z]+ NAME Pointer to pointer arithmetic and dereference PROG uprobe:./testprogs/array_access:test_ptr_array { $p = (int32 **)arg0; @x = **($p + 1); exit(); } EXPECT @x: 2 AFTER ./testprogs/array_access NAME Pointer is truthy for if conditions PROG BEGIN { if (curtask) { @ = 1; } exit(); } EXPECT @: 1 NAME Pointer is truthy for tenary expressions PROG BEGIN { @ = curtask ? 1 : 2; exit(); } EXPECT @: 1 NAME Pointer is useable in logical and expressions PROG BEGIN { @ = (curtask && 0) ? 1 : 2; exit(); } EXPECT @: 2 NAME Pointer is useable in logical or expressions PROG BEGIN { @ = (curtask || 0) ? 1 : 2; exit(); } EXPECT @: 1 bpftrace-0.23.2/tests/runtime/precedence000066400000000000000000000005061477746507000202530ustar00rootroot00000000000000NAME operator_precedence_1 PROG BEGIN { print(1 + 2 * 3 ); exit() } EXPECT 7 NAME operator_precedence_2 PROG BEGIN { print(1 + 2 * 3 + 1); exit() } EXPECT 8 NAME operator_precedence_3 PROG BEGIN { print(1 + 2 * 3 + 1 * 10); exit() } EXPECT 17 NAME operator_precedence_3 PROG BEGIN { print(1 + 2 / 2 * 3); exit() } EXPECT 4 bpftrace-0.23.2/tests/runtime/probe000066400000000000000000000406371477746507000172760ustar00rootroot00000000000000NAME fentry PROG fentry:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* REQUIRES_FEATURE fentry AFTER ./testprogs/syscall read NAME fexit PROG fexit:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* REQUIRES_FEATURE fentry AFTER ./testprogs/syscall read NAME fentry_wildcard PROG fentry:vfs_re*ad { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* REQUIRES_FEATURE fentry AFTER ./testprogs/syscall read NAME fentry_module PROG fentry:vmlinux:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* REQUIRES_FEATURE fentry AFTER ./testprogs/syscall read NAME fentry_module_wildcard PROG fentry:vmlinu*:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* REQUIRES_FEATURE fentry AFTER ./testprogs/syscall read # https://github.com/bpftrace/bpftrace/issues/2447 NAME fentry_multiple_attach_point_multiple_functions PROG BEGIN { @a = 2; @b = 1; @c = 1 } i:s:1 { exit() } fentry:vfs_read, fentry:vfs_open /@a != 0/ { @a -= 1 } fentry:vfs_read /@b == 1/ { @b = 0 } fentry:vfs_open /@c == 1/ { @c = 0 } EXPECT @a: 0 @b: 0 @c: 0 REQUIRES_FEATURE fentry # Note: this calls both vfs_read and vfs_open AFTER ./testprogs/syscall read NAME fentry args PROG fentry:vfs_read { printf("%d\n", args.count); exit(); } EXPECT_REGEX [0-9][0-9]* REQUIRES_FEATURE fentry REQUIRES_FEATURE btf AFTER ./testprogs/syscall read # Checking backwards compatibility NAME fentry args as a pointer PROG fentry:vfs_read { printf("%d\n", args->count); exit(); } EXPECT_REGEX [0-9][0-9]* REQUIRES_FEATURE fentry REQUIRES_FEATURE btf AFTER ./testprogs/syscall read NAME fentry func builtin PROG fentry:vfs_read { printf("func: %s\n", func); exit(); } EXPECT func: vfs_read REQUIRES_FEATURE fentry REQUIRES_FEATURE btf REQUIRES_FEATURE get_func_ip AFTER ./testprogs/syscall read NAME fexit large args PROG fexit:__sys_bpf { if (args.cmd == 1111 && args.size == 2222 && (int64)retval == -7) { printf("SUCCESS %d\n", pid); exit(); }} EXPECT_REGEX SUCCESS [0-9][0-9]* REQUIRES_FEATURE fentry REQUIRES_FEATURE btf AFTER ./testprogs/fentry_args 1111 2222 NAME fentry_warn_missing_probes PROG fentry:vfs_read,fentry:nonsense { exit(); } EXPECT WARNING: No BTF found for nonsense, skipping. NAME fentry_ignore_missing_probes PROG config = { missing_probes = "ignore" } fentry:vfs_read,fentry:nonsense { exit(); } EXPECT_NONE WARNING: No BTF found for nonsense, skipping. NAME fentry_error_missing_probes PROG config = { missing_probes = "error" } fentry:vfs_read,fentry:nonsense { exit(); } EXPECT ERROR: No BTF found for nonsense WILL_FAIL # Sanity check for kfunc/kretfunc alias NAME kfunc PROG kfunc:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* REQUIRES_FEATURE fentry AFTER ./testprogs/syscall read NAME kretfunc PROG kretfunc:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* REQUIRES_FEATURE fentry AFTER ./testprogs/syscall read NAME kprobe PROG kprobe:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall read NAME kprobe_short_name PROG k:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall read NAME kprobe_order RUN {{BPFTRACE}} runtime/scripts/kprobe_order.bt EXPECT first second AFTER /bin/bash -c "./testprogs/syscall nanosleep 1001"; NAME kprobe_offset PROG kprobe:vfs_read+0 { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall read NAME kprobe_wildcard PROG kprobe:vmlinux:vfs_rea* { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall read NAME kprobe_wildcard_multi RUN {{BPFTRACE}} --unsafe -e 'kprobe:ksys_* { printf("progs: "); system("bpftool prog | grep kprobe | grep ksys_ | wc -l"); exit(); }' EXPECT progs: 1 REQUIRES bpftool REQUIRES_FEATURE kprobe_multi NAME kprobe_module PROG kprobe:vmlinux:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall read NAME kprobe_module_wildcard PROG kprobe:vmlinu*:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall read NAME kprobe_module_missing PROG kprobe:nonsense:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT ERROR: Error attaching probe: kprobe:nonsense:vfs_read: specified module nonsense in probe kprobe:nonsense:vfs_read is not loaded. WILL_FAIL NAME kprobe_wildcard_all_leading_underscore PROG kprobe:_* { } interval:s:1{ printf("SUCCESS"); exit(); } EXPECT_REGEX SUCCESS ENV BPFTRACE_MAX_PROBES=20000 REQUIRES_FEATURE kprobe_multi NAME kprobe_disallow_rcu_functions PROG kprobe:rcu_* { printf("SUCCESS %d\n", pid); exit(); } EXPECT No probes to attach WILL_FAIL NAME kprobe_hide_ftrace_invalid_address RUN {{BPFTRACE}} -l | grep -c __ftrace_invalid_address__ || true EXPECT 0 NAME kprobe_func_missing PROG kprobe:vmlinux:nonsense { printf("SUCCESS %d\n", pid); exit(); } EXPECT ERROR: Error attaching probe: kprobe:vmlinux:nonsense WILL_FAIL NAME kprobe_multi_wildcard RUN {{BPFTRACE}} --unsafe -e 'kprobe:ksys_* { printf("link: "); system("bpftool link | grep kprobe_multi | wc -l"); exit(); }' EXPECT link: 1 REQUIRES bpftool REQUIRES_FEATURE kprobe_multi NAME uprobe_multi_wildcard RUN {{BPFTRACE}} --unsafe -e 'uprobe:./testprogs/uprobe_test:uprobeFunction* { printf("link: "); system("bpftool link | grep -E \"uprobe_multi|type 12\" | wc -l"); exit(); }' EXPECT link: 1 REQUIRES bpftool REQUIRES_FEATURE uprobe_multi AFTER ./testprogs/uprobe_test NAME kprobe_wildcard probe builtin RUN {{BPFTRACE}} --unsafe -e 'kprobe:ksys_* { @ = probe; printf("progs: "); system("bpftool prog | grep kprobe | grep ksys_ | wc -l"); exit(); }' EXPECT_REGEX progs: [1-9][0-9]+ REQUIRES bpftool NAME kprobe_multi_mixed_attachpoint PROG kprobe:vfs_read,kprobe:nonsense* { printf("SUCCESS\n"); exit(); } EXPECT SUCCESS REQUIRES_FEATURE kprobe_multi AFTER ./testprogs/syscall read NAME kprobe_offset_module RUN {{BPFTRACE}} -e 'kprobe:nf_tables_newtable+0x4 { printf("hit\n"); exit(); }' AFTER nft add table bpftrace EXPECT hit ARCH x86_64 REQUIRES lsmod | grep '^nf_tables' REQUIRES nft --help # Proxy for testing if running virtualized. The offset is specific to our pre-built kernels. REQUIRES test -d /mnt/vmtest CLEANUP nft delete table bpftrace # Local entry point to nf_tables_newtable is located at offset of 8 bytes in ppc64 and aarch64. NAME kprobe_offset_module RUN {{BPFTRACE}} -e 'kprobe:nf_tables_newtable+0x8 { printf("hit\n"); exit(); }' AFTER nft add table bpftrace EXPECT hit ARCH ppc64|ppc64le|aarch64 REQUIRES lsmod | grep '^nf_tables' REQUIRES nft --help CLEANUP nft delete table bpftrace NAME kprobe_offset_module_error RUN {{BPFTRACE}} -e 'kprobe:nft_trans_alloc_gfp+0x1 { printf("hit\n"); exit(); }' EXPECT ERROR: Possible attachment attempt in the middle of an instruction, try a different offset. ARCH x86_64 REQUIRES lsmod | grep '^nf_tables' WILL_FAIL NAME kprobe_offset_module_error RUN {{BPFTRACE}} -e 'kprobe:nft_trans_alloc_gfp+0x1 { printf("hit\n"); exit(); }' EXPECT cannot attach kprobe, Invalid argument ARCH aarch64|ppc64le|ppc64 REQUIRES lsmod | grep '^nf_tables' WILL_FAIL NAME kprobe_warn_missing_probes PROG kprobe:vfs_read,kprobe:nonsense { exit(); } EXPECT WARNING: could not attach probe kprobe:nonsense, skipping. NAME kprobe_ignore_missing_probes PROG config = { missing_probes = "ignore" } kprobe:vfs_read,kprobe:nonsense { exit(); } EXPECT_NONE WARNING: could not attach probe kprobe:nonsense, skipping. NAME kprobe_error_missing_probes PROG config = { missing_probes = "error" } kprobe:vfs_read,kprobe:nonsense { exit(); } EXPECT ERROR: Error attaching probe: kprobe:nonsense WILL_FAIL NAME kretprobe PROG kretprobe:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall read NAME kretprobe_short_name PROG kr:vfs_read { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall read NAME kretprobe_order RUN {{BPFTRACE}} runtime/scripts/kretprobe_order.bt EXPECT first second AFTER /bin/bash -c "./testprogs/syscall nanosleep 1000"; NAME kretprobe_wildcard PROG kretprobe:vmlinux:vfs_rea* { printf("SUCCESS %d\n", pid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall read NAME kretprobe_wildcard_multi RUN {{BPFTRACE}} --unsafe -e 'kretprobe:ksys_* { system("bpftool prog | grep kprobe | grep ksys_ | wc -l"); exit(); }' EXPECT 1 REQUIRES bpftool REQUIRES_FEATURE kprobe_multi NAME kretprobe_wildcard_multi_link RUN {{BPFTRACE}} --unsafe -e 'kretprobe:ksys_* { printf("link: "); system("bpftool link | grep kprobe_multi | wc -l"); exit(); }' EXPECT link: 1 REQUIRES bpftool REQUIRES_FEATURE kprobe_multi NAME uretprobe_multi_wildcard RUN {{BPFTRACE}} --unsafe -e 'uretprobe:./testprogs/uprobe_test:uprobeFunction* { printf("link: "); system("bpftool link | grep -E \"uprobe_multi|type 12\" | wc -l"); exit(); }' EXPECT link: 1 REQUIRES bpftool REQUIRES_FEATURE uprobe_multi AFTER ./testprogs/uprobe_test NAME uprobe_multi_wildcard_target_wildcard RUN {{BPFTRACE}} --unsafe -e 'uretprobe:*:uprobeFunction* { printf("link: "); system("bpftool link | grep -E \"uprobe_multi|type 12\" | wc -l"); exit(); }' -p {{BEFORE_PID}} EXPECT link: 1 REQUIRES bpftool REQUIRES_FEATURE uprobe_multi BEFORE ./testprogs/uprobe_test NAME uprobe PROG uprobe:/bin/bash:echo_builtin { printf("arg0: %d\n", arg0); exit();} EXPECT_REGEX arg0: [0-9]* AFTER /bin/bash -c "echo lala" NAME uprobe_offset PROG uprobe:/bin/bash:echo_builtin+0 { printf("arg0: %d\n", arg0); exit();} EXPECT_REGEX arg0: [0-9]* AFTER /bin/bash -c "echo lala" NAME uprobe_offset PROG uprobe:/bin/bash:"echo_builtin"+0 { printf("arg0: %d\n", arg0); exit();} EXPECT_REGEX arg0: [0-9]* AFTER /bin/bash -c "echo lala" NAME uprobe_offset_fail_size PROG uprobe:/bin/bash:echo_builtin+1000000 { printf("arg0: %d\n", arg0); exit();} EXPECT_REGEX Offset outside the function bounds \('echo_builtin' size is* WILL_FAIL NAME uprobe_address_fail_resolve PROG uprobe:/bin/bash:10 { printf("arg0: %d\n", arg0); exit();} EXPECT ERROR: Could not resolve address: /bin/bash:0xa WILL_FAIL NAME uprobe_library PROG uprobe:libc:fread { printf("size: %d\n", arg1); exit(); } EXPECT_REGEX size: [0-9]+ AFTER ./testprogs/uprobe_library REQUIRES_FEATURE libpath_resolv NAME uprobe_order RUN {{BPFTRACE}} runtime/scripts/uprobe_order.bt EXPECT first second AFTER /bin/bash -c "echo lala"; NAME uprobe_zero_size PROG uprobe:./testprogs/uprobe_test:_init { printf("arg0: %d\n", arg0); exit();} EXPECT ERROR: Could not determine boundary for _init (symbol has size 0). Use --unsafe to force attachment. WARNING: This option could lead to data corruption in the target process. WILL_FAIL NAME uprobe_zero_size_unsafe RUN {{BPFTRACE}} --unsafe -e 'uprobe:./testprogs/uprobe_test:_init { printf("arg0: %d\n", arg0); exit();}' EXPECT_REGEX arg0: [0-9]* AFTER ./testprogs/uprobe_test NAME uprobe_warn_missing_probes PROG uprobe:./testprogs/uprobe_test:uprobeFunction1,uprobe:./testprogs/uprobe_test:uprobeFunction3 { exit(); } EXPECT WARNING: Could not resolve symbol: ./testprogs/uprobe_test:uprobeFunction3, skipping probe. AFTER ./testprogs/uprobe_test NAME uprobe_ignore_missing_probes PROG config = { missing_probes = "ignore" } uprobe:./testprogs/uprobe_test:uprobeFunction1,uprobe:./testprogs/uprobe_test:uprobeFunction3 { exit(); } EXPECT_NONE WARNING: Could not resolve symbol: ./testprogs/uprobe_test:uprobeFunction3, skipping probe. AFTER ./testprogs/uprobe_test NAME uprobe_error_missing_probes PROG config = { missing_probes = "error" } uprobe:./testprogs/uprobe_test:uprobeFunction1,uprobe:./testprogs/uprobe_test:uprobeFunction3 { exit(); } EXPECT ERROR: Could not resolve symbol: ./testprogs/uprobe_test:uprobeFunction3, cannot attach probe. AFTER ./testprogs/uprobe_test WILL_FAIL NAME uretprobe PROG uretprobe:/bin/bash:echo_builtin { printf("readline: %d\n", retval); exit();} EXPECT_REGEX readline: [0-9]* AFTER /bin/bash -c "echo lala" NAME uretprobe_order RUN {{BPFTRACE}} runtime/scripts/uretprobe_order.bt EXPECT first second AFTER /bin/bash -c "echo lala"; NAME tracepoint PROG tracepoint:syscalls:sys_exit_nanosleep { printf("SUCCESS %d\n", gid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall nanosleep 1e8 NAME tracepoint_short_name PROG t:syscalls:sys_exit_nanosleep { printf("SUCCESS %d\n", gid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall nanosleep 1e8 NAME tracepoint_order RUN {{BPFTRACE}} runtime/scripts/tracepoint_order.bt EXPECT first second AFTER ./testprogs/syscall nanosleep 100000001; NAME tracepoint_expansion RUN {{BPFTRACE}} -e 'tracepoint:syscalls:sys_*_nanosleep { printf("hit\n"); }' -c "./testprogs/syscall nanosleep 1e8" EXPECT hit NAME tracepoint_missing PROG t:syscalls:nonsense { print("hit"); exit(); } EXPECT stdin:1:1-20: ERROR: tracepoint not found: syscalls:nonsense WILL_FAIL NAME tracepoint_multiattach_missing PROG t:syscalls:sys_exit_nanosleep,t:syscalls:nonsense { print("hit"); exit(); } EXPECT hit AFTER ./testprogs/syscall nanosleep 1e8 WILL_FAIL NAME tracepoint args PROG tracepoint:syscalls:sys_enter_read { printf("%d\n", args.count); exit(); } EXPECT_REGEX [0-9][0-9]* AFTER ./testprogs/syscall read # Checking backwards compatibility NAME tracepoint args as pointer PROG tracepoint:syscalls:sys_enter_read { printf("%d\n", args->count); exit(); } EXPECT_REGEX [0-9][0-9]* AFTER ./testprogs/syscall read # Test that we get at least two characters out NAME tracepoint_data_loc PROG tracepoint:irq:irq_handler_entry { print(str(args.name)); exit(); } EXPECT_REGEX ..+ NAME rawtracepoint PROG rawtracepoint:sys_exit { printf("SUCCESS %d\n", gid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall nanosleep 1e8 NAME rawtracepoint_short_name PROG rt:sys_exit { printf("SUCCESS %d\n", gid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall nanosleep 1e8 NAME rawtracepoint_missing PROG rawtracepoint:nonsense { print("hit"); exit(); } EXPECT ERROR: Probe does not exist: rawtracepoint:nonsense WILL_FAIL NAME rawtracepoint_wildcard PROG rawtracepoint:sys_* { printf("SUCCESS %d\n", gid); exit(); } EXPECT_REGEX SUCCESS [0-9][0-9]* AFTER ./testprogs/syscall nanosleep NAME profile PROG profile:hz:599 { print("hit"); exit(); } EXPECT hit NAME profile_short_name PROG p:hz:599 { print("hit"); exit(); } EXPECT hit NAME profile_probe_builtin PROG profile:hz:599 { print(probe); exit();} EXPECT profile:hz:599 NAME interval PROG interval:ms:1 { print("hit"); exit(); } EXPECT hit NAME interval_short_name PROG i:ms:1{ print("hit"); exit();} EXPECT hit NAME interval_probe_builtin PROG interval:ms:1 { print(probe); exit();} EXPECT interval:ms:1 NAME software PROG software:cpu-clock:100000 { print("hit"); exit();} EXPECT hit NAME software_probe_builtin PROG software:cpu-clock:100000 { print(probe); exit();} EXPECT software:cpu-clock:100000 NAME software_alias_probe_builtin PROG software:cpu:100000 { print(probe); exit();} EXPECT software:cpu:100000 NAME hardware REQUIRES ls /sys/devices/cpu/events/cache-misses PROG hardware:cache-misses:10 { print("hit"); exit(); } EXPECT hit NAME hardware_probe_builtin REQUIRES ls /sys/devices/cpu/events/cache-misses PROG hardware:cache-misses:10 { print(probe); exit();} EXPECT hardware:cache-misses:10 NAME probe_builtin_string_compare PROG uprobe:testprogs/uprobe_test:uprobeFunction* { if (strcontains(probe, "uprobeFunction2")) { print(probe); exit(); } } AFTER ./testprogs/uprobe_test EXPECT uprobe:testprogs/uprobe_test:uprobeFunction2 NAME probe_builtin_scratch_buf PROG config = { on_stack_limit = 0 } software:cpu:100000 { print(probe); exit(); } EXPECT software:cpu:100000 NAME BEGIN PROG BEGIN { printf("Hello\n"); exit();} EXPECT Hello TIMEOUT 2 NAME END_processing_after_exit PROG interval:s:1 { exit(); } END { printf("end"); } EXPECT end TIMEOUT 2 NAME BEGIN,END PROG BEGIN,END { printf("Hello\n"); exit(); } EXPECT Hello Hello 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_REGEX ERROR: Failed to compile: Your program is trying to generate more than \d+ BPF programs, which exceeds the current limit of 1024.\nYou can increase the limit through the BPFTRACE_MAX_BPF_PROGS environment variable. WILL_FAIL TIMEOUT 2 NAME sanitise probe name PROG uprobe:./testprogs/uprobe_namesan:fn* { printf("ok\n"); exit(); } EXPECT ok AFTER ./testprogs/uprobe_namesan bpftrace-0.23.2/tests/runtime/regression000066400000000000000000000113641477746507000203420ustar00rootroot00000000000000# https://github.com/bpftrace/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("_%s_\n", "done"); exit(); } EXPECT _done_ TIMEOUT 1 NAME logical_and_or_different_sizes PROG struct Foo { int m; } uprobe:./testprogs/simple_struct:func { $foo = (struct Foo*)arg0; printf("%d %d %d %d %d\n", $foo->m, $foo->m && 0, 1 && $foo->m, $foo->m || 0, 0 || $foo->m); exit(); } AFTER ./testprogs/simple_struct EXPECT 2 0 1 1 1 # https://github.com/bpftrace/bpftrace/issues/759 # Update test once https://github.com/bpftrace/bpftrace/pull/781 lands # NAME ntop_map_printf # RUN {{BPFTRACE}} -e 'union ip { char v4[4]; char v6[16]; } uprobe:./testprogs/ntop:test_ntop { @a = ntop(2, ((union ip*)arg0)->v4); printf("v4: %s; ", @a); @b = ntop(10, ((union ip*)arg1)->v6); exit() } END { printf("v6: %s", @b); clear(@a); clear(@b) }' # AFTER ./testprogs/ntop # EXPECT v4: 127.0.0.1; v6: ffee:ffee:ddcc:ddcc:bbaa:bbaa:c0a8:1 # TIMEOUT 1 NAME modulo_operator PROG BEGIN { @x = 4; printf("%d\n", @x % 2); exit() } EXPECT 0 NAME c_array_indexing RUN {{BPFTRACE}} -e 'struct Foo { int a; uint8_t b[10]; } uprobe:testprogs/uprobe_test:uprobeFunction2 { $foo = (struct Foo *)arg0; printf("%c %c %c %c %c\n", $foo->b[0], $foo->b[1], $foo->b[2], $foo->b[3], $foo->b[4]) }' -c ./testprogs/uprobe_test EXPECT h e l l o # https://github.com/bpftrace/bpftrace/issues/1773 NAME single_arg_wildcard_listing RUN {{BPFTRACE}} -l "*do_nanosleep*" EXPECT kprobe:do_nanosleep TIMEOUT 1 NAME single_arg_wildcard_listing_tracepoint RUN {{BPFTRACE}} -l "*sched_switch*" EXPECT tracepoint:sched:sched_switch TIMEOUT 1 # https://github.com/bpftrace/bpftrace/issues/1952 NAME async_id_invalid_probe_expansion PROG kprobe:zzzzz { probe; printf("asdf\n") } BEGIN { printf("_%s_\n", "success"); exit() } EXPECT _success_ TIMEOUT 1 NAME strncmp_n_longer_than_buffer PROG BEGIN { printf("_%d_\n", strncmp("wow", comm, 17)); exit() } EXPECT _1_ TIMEOUT 1 NAME strncmp_checks_for_nul PROG BEGIN { printf("_%d_\n", strncmp("one", "one ", 4)); exit() } EXPECT _1_ TIMEOUT 1 NAME string compare with empty PROG BEGIN { if ("hello" == "") { printf("equal"); } else { printf("not equal"); } exit(); } EXPECT not equal TIMEOUT 3 NAME string compare with prefix PROG BEGIN { if ("hello" == "hell") { printf("equal"); } else { printf("not equal"); } exit(); } EXPECT not equal TIMEOUT 3 NAME strncmp with prefix PROG BEGIN { if (strncmp("hello", "hell", 5) == 0) { printf("equal"); } else { printf("not equal"); } exit(); } EXPECT not equal TIMEOUT 3 # The issue this test catches is the verifier doesn't allow multiple ALU ops on ctx pointer. # ctx+const is ok, but ctx+const+const is not ok. NAME access ctx struct field twice PROG tracepoint:sched:sched_wakeup { if (args.comm == "asdf") { print(args.comm) } exit(); } EXPECT Attaching 1 probe... TIMEOUT 1 # https://github.com/bpftrace/bpftrace/issues/2135 NAME address_probe_invalid_expansion RUN {{BPFTRACE}} -e "uprobe:./testprogs/uprobe_test:0x$(nm ./testprogs/uprobe_test | awk '$3 == "uprobeFunction1" {print $1}') { @[probe] = count(); exit() }" EXPECT Attaching 1 probe... TIMEOUT 1 # The verifier is not able to track BTF info for double pointer dereferences and # array accesses so we must use bpf_probe_read_kernel, otherwise the program is # rejected. The below two tests make sure that we handle such situations. NAME fentry double pointer dereference PROG fentry:__module_get { print((*args.module->trace_events)->flags); exit(); } AFTER lsmod REQUIRES_FEATURE fentry EXPECT Attaching 1 probe... TIMEOUT 1 NAME fentry double pointer array access PROG fentry:__module_get { print(args.module->trace_events[1]->flags); exit(); } AFTER lsmod REQUIRES_FEATURE fentry EXPECT Attaching 1 probe... TIMEOUT 1 NAME fentry array access via pointer PROG fentry:__module_get { print(args.module->version[0]); exit(); } AFTER lsmod REQUIRES_FEATURE fentry EXPECT Attaching 1 probe... TIMEOUT 1 NAME unaligned key with aligned value PROG BEGIN { @mapA["aaaabbb", 0] = 1; @mapA["ccccdddd", 0] = 1; } EXPECT Attaching 1 probe... TIMEOUT 1 # In some cases, LLVM can generate code that violates the BPF verifier # constraint that the context pointer is not modified prior to access (see # #3603). #3629 introduces the use of intrinsics that should ensure that this # doesn't happen, but this runtime test exercises a pattern that previously # produced code that failed to verify. NAME preserve context pointer PROG BEGIN { @test[1] = (uint64)1; } tracepoint:syscalls:sys_enter_kill { if (strcontains(comm, "test")) { @test[(uint64)args.pid] = 1; } if (args.pid == @test[1]) { print((1)); } if (args.pid == @test[1]) { print((1)); exit(); } } EXPECT Attaching 2 probes... TIMEOUT 1 bpftrace-0.23.2/tests/runtime/scripts/000077500000000000000000000000001477746507000177215ustar00rootroot00000000000000bpftrace-0.23.2/tests/runtime/scripts/hello_world.bt000066400000000000000000000000641477746507000225620ustar00rootroot00000000000000BEGIN { printf("hello world!\n"); exit(); } bpftrace-0.23.2/tests/runtime/scripts/interval_order.bt000066400000000000000000000000741477746507000232700ustar00rootroot00000000000000interval:ms:1000 {} interval:s:1 {} interval:us:100000 {} bpftrace-0.23.2/tests/runtime/scripts/kprobe_order.bt000066400000000000000000000003511477746507000227240ustar00rootroot00000000000000// Check arg0 to make sure these are the nanosleeps we issued kprobe:hrtimer_nanosleep { if (arg0 == 1001) { printf("first"); } } kprobe:hrtimer_nanosleep { if (arg0 == 1001) { printf(" second\n"); exit(); } } bpftrace-0.23.2/tests/runtime/scripts/kretprobe_order.bt000066400000000000000000000001651477746507000234420ustar00rootroot00000000000000kretprobe:hrtimer_nanosleep { printf("first"); } kretprobe:hrtimer_nanosleep { printf(" second\n"); exit(); } bpftrace-0.23.2/tests/runtime/scripts/parallel_map_access.bt000066400000000000000000000007331477746507000242250ustar00rootroot00000000000000// Regression testing the map's thread safety. // Bpftrace shouldn't crash during map's print/clear/zero operations // while delete is called at the same time. // // For more information, please refer to: // https://github.com/bpftrace/bpftrace/pull/2623 i:us:1 { @data[rand % 100] = count(); if (rand % 5 == 0) { delete(@data, rand % 10); } } i:ms:1 { print(@data); clear(@data); zero(@data); } i:s:1 { print("SUCCESS"); exit(); } bpftrace-0.23.2/tests/runtime/scripts/struct_array.bt000077500000000000000000000013231477746507000227740ustar00rootroot00000000000000struct T { uint32_t a; uint32_t b[2]; }; struct W { uint32_t a; struct T t; }; struct C { uint32_t a; void * b; struct W w[10]; }; uprobe:./testprogs/struct_array:clear { $c = (struct C *) arg0; printf("%d ", $c->w[0].a); printf("%d ", $c->w[2].a); printf("%d ", $c->w[4].a); printf("%d ", $c->w[6].a); printf("%d ", $c->w[8].a); printf("-- "); printf("%d ", $c->w[1].t.a); printf("%d ", $c->w[3].t.a); printf("%d ", $c->w[5].t.a); printf("%d ", $c->w[7].t.a); printf("%d ", $c->w[9].t.a); printf("-- "); printf("%d ", $c->w[0].t.b[1]); printf("%d ", $c->w[1].t.b[1]); printf("%d ", $c->w[2].t.b[1]); printf("%d ", $c->w[3].t.b[1]); printf("%d\n", $c->w[4].t.b[1]); }bpftrace-0.23.2/tests/runtime/scripts/struct_array_vars.bt000077500000000000000000000014411477746507000240300ustar00rootroot00000000000000struct T { uint32_t a; uint32_t b[2]; }; struct W { uint32_t a; struct T t; }; struct C { uint32_t a; void * b; struct W w[10]; }; uprobe:./testprogs/struct_array:clear { $c = (struct C *) arg0; $w = $c->w; @w[0] = $c->w; @[$c->w] = 42; printf("%d ", $w[0].a); printf("%d ", $w[2].a); printf("%d ", $w[4].a); printf("%d ", $w[6].a); printf("%d ", $w[8].a); printf("-- "); printf("%d ", @w[0][1].t.a); printf("%d ", @w[0][3].t.a); printf("%d ", @w[0][5].t.a); printf("%d ", @w[0][7].t.a); printf("%d ", @w[0][9].t.a); printf("-- "); printf("%d ", @w[0][0].t.b[1]); printf("%d ", @w[0][1].t.b[1]); printf("%d ", @w[0][2].t.b[1]); printf("%d ", @w[0][3].t.b[1]); printf("%d ", @w[0][4].t.b[1]); printf("-- "); printf("%d ", @[$c->w]); }bpftrace-0.23.2/tests/runtime/scripts/struct_let.bt000077500000000000000000000006301477746507000224420ustar00rootroot00000000000000struct T { uint32_t a; uint32_t b[2]; }; struct W { uint32_t a; struct T t; }; struct C { uint32_t a; void * b; struct W w[10]; }; uprobe:./testprogs/struct_array:clear { let $c: struct C *; let $w: struct W[10]; $c = (struct C *) arg0; $w = $c->w; printf("%d ", $w[0].a); printf("%d ", $w[2].a); printf("%d ", $w[4].a); printf("%d ", $w[6].a); printf("%d ", $w[8].a); } bpftrace-0.23.2/tests/runtime/scripts/struct_walk.bt000066400000000000000000000006331477746507000226140ustar00rootroot00000000000000struct C { uint32_t a; uint64_t b; }; uprobe:./testprogs/struct_walk:clear { $c = (struct C *) arg0; $size = (uint32) arg1; if ($size != 10) { print("Size should be 10"); exit(); } /* Verifier doesn't know size is always 10 * we have to clamp it */ $i = 10; while ($i-- > 0) { @a += $c->a; @b += ($c++)->b; } printf("a: %d b: %d\n", @a, @b); clear(@a); clear(@b); } bpftrace-0.23.2/tests/runtime/scripts/tracepoint_order.bt000066400000000000000000000005451477746507000236170ustar00rootroot00000000000000// Check tv_nsec to make sure these are the nanosleeps we issued tracepoint:syscalls:sys_enter_nanosleep { $tv_nsecs = (*args.rqtp).tv_nsec; if ($tv_nsecs == 100000001) { printf("first"); } } tracepoint:syscalls:sys_enter_nanosleep { $tv_nsecs = (*args.rqtp).tv_nsec; if ($tv_nsecs == 100000001) { printf(" second\n"); exit(); } } bpftrace-0.23.2/tests/runtime/scripts/uprobe_order.bt000066400000000000000000000001711477746507000227360ustar00rootroot00000000000000uprobe:/bin/bash:echo_builtin { printf("first"); } uprobe:/bin/bash:echo_builtin { printf(" second\n"); exit(); } bpftrace-0.23.2/tests/runtime/scripts/uprobe_pid_check.bt000066400000000000000000000007151477746507000235400ustar00rootroot00000000000000BEGIN { @count = 0; } uprobe:./testprogs/uprobe_fork_loop:uprobeFunction1 { if (pid == $1) { // counting 10 instances of the expected pid // is similar to a delay to ensure we never get // an unexpected pid if (@count == 10) { print("hello"); exit(); } else { @count++; } } else { // pid should always be the first positional param exit(); } } bpftrace-0.23.2/tests/runtime/scripts/uretprobe_order.bt000066400000000000000000000001771477746507000234570ustar00rootroot00000000000000uretprobe:/bin/bash:echo_builtin { printf("first"); } uretprobe:/bin/bash:echo_builtin { printf(" second\n"); exit(); } bpftrace-0.23.2/tests/runtime/scripts/usdt_file_activation_multiprocess.bt000066400000000000000000000006611477746507000272630ustar00rootroot00000000000000BEGIN { @count = 0; } usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { // Ensure each process only increments @count once if (@pids[pid] == 0) { @count++; @pids[pid] = 1; } // Hacky, but easiest way to get script to exit quickly for tests if (@count == 2) { exit(); } } END { printf("found %d processes\n", @count); clear(@pids); clear(@count); } bpftrace-0.23.2/tests/runtime/scripts/usdt_multi_modules.bt000066400000000000000000000001751477746507000241740ustar00rootroot00000000000000usdt:./testprogs/usdt_test:tracetest:testprobe { exit(); } usdt:./testprogs/usdt_sized_args:test:probe1 { exit(); } bpftrace-0.23.2/tests/runtime/scripts/variable_scope.bt000066400000000000000000000001521477746507000232240ustar00rootroot00000000000000BEGIN { if (1) { $x = 1; } if (1) { let $x; if (0) { $x = 5; } print($x); } exit(); } bpftrace-0.23.2/tests/runtime/scripts/variable_scope_late_decl.bt000066400000000000000000000002651477746507000252250ustar00rootroot00000000000000BEGIN { @a[1] = 1; if (1) { $x = 1; print(("a", $x)); } for ($kv : @a) { $x = 2; print(("b", $x)); } let $x; if (0) { $x = 3; } print(("c", $x)); exit(); } bpftrace-0.23.2/tests/runtime/scripts/watchpoint_multiattach.bt000066400000000000000000000002641477746507000250310ustar00rootroot00000000000000BEGIN { @count = 0; } watchpoint:increment_0+arg0:4:w, watchpoint:increment_1+arg0:4:w, watchpoint:increment_2+arg0:4:w { @count++; } END { printf("count=%d\n", @count); } bpftrace-0.23.2/tests/runtime/scripts/watchpoint_unwatch.bt000066400000000000000000000003521477746507000241610ustar00rootroot00000000000000BEGIN { @count = 0; } uprobe:./testprogs/watchpoint_unwatch:increment { @addr = (uint64)arg0; } watchpoint:increment+arg0:4:w { printf("hello world\n"); @count++; unwatch(@addr); } END { printf("count=%d\n", @count); } bpftrace-0.23.2/tests/runtime/selftest000066400000000000000000000004531477746507000200100ustar00rootroot00000000000000# This file contains selftests for runtime engine NAME command doesn't respond to SIGTERM # The __BPFTRACE_NOTIFY_PROBES_ATTACHED is needed to disable attach timeout RUN trap '' TERM; echo __BPFTRACE_NOTIFY_PROBES_ATTACHED && sleep 999 && echo "shouldn't echo" TIMEOUT 1 EXPECT_NONE shouldn't echo bpftrace-0.23.2/tests/runtime/signals000066400000000000000000000010631477746507000176150ustar00rootroot00000000000000NAME 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 custom signal handling probe RUN {{BPFTRACE}} -e 'self:signal:SIGUSR1 { print("signal handler"); exit(); }' AFTER kill -s USR1 $(pidof bpftrace) EXPECT signal handler bpftrace-0.23.2/tests/runtime/signed_ints000066400000000000000000000016441477746507000204700ustar00rootroot00000000000000NAME stats with negative values PROG BEGIN { @=stats(-10); @=stats(-5); @=stats(5); exit() } EXPECT @: count 3, average -3, total -10 NAME avg with negative values PROG BEGIN { @=avg(-30); @=avg(-10); exit(); } EXPECT @: -20 NAME avg cast negative values PROG BEGIN { @ = avg(-1); @ = avg(-1); @ = avg(-10); if (@ == -4) { printf("done\n"); exit(); }} EXPECT done NAME negative map value PROG BEGIN { @ = -11; exit(); } EXPECT @: -11 TIMEOUT 1 NAME sum negative maps PROG BEGIN { @ = -11; @+=@; exit() } EXPECT @: -22 TIMEOUT 1 NAME Comparison should print as 0 or 1 PROG struct x { uint64_t x; }; BEGIN { $a = (*(struct x*)0).x; printf("%d %d\n", $a > -1, $a < 1); exit(); } EXPECT 0 1 TIMEOUT 1 NAME sum with negative value PROG BEGIN { @=sum(10); @=sum(-20); exit(); } EXPECT @: -10 TIMEOUT 1 NAME mixed values PROG BEGIN { printf("%d %d %d %d\n", (int8) -10, -5555, (int16)-123, 100); exit(); } EXPECT -10 -5555 -123 100 bpftrace-0.23.2/tests/runtime/strcontains000066400000000000000000000055551477746507000205360ustar00rootroot00000000000000NAME path PROG fentry:security_file_open { if (strcontains(path(args.file->f_path, 128), "tmp")) { printf("OK\n"); exit(); } } EXPECT OK REQUIRES_FEATURE dpath fentry AFTER TMPDIR=/tmp ./testprogs/syscall open NAME literals PROG BEGIN { if (strcontains("abc", "a")) { printf("OK\n"); exit(); } } EXPECT OK TIMEOUT 1 NAME no literals PROG tracepoint:syscalls:sys_enter_execve / str(args.argv[0]) == "./testprogs/true" / { print(strcontains(str(args.argv[1]), str(args.argv[2]))); exit(); } AFTER ./testprogs/true zztest test EXPECT 1 TIMEOUT 1 # Clamping is done to not blow verifier complexity limits ENV BPFTRACE_MAX_STRLEN=64 NAME string truncation does not affect matching PROG tracepoint:syscalls:sys_enter_execve / str(args.argv[0]) == "./testprogs/true" / { print(strcontains(str(args.argv[1], 6), str(args.argv[2], 4))); exit(); } AFTER ./testprogs/true zztest test EXPECT 1 NAME basic substring match PROG BEGIN { print(strcontains("hello-test-world", "test")); exit(); } EXPECT 1 TIMEOUT 1 NAME substring at end PROG BEGIN { print(strcontains("hello-test-world", "world")); exit(); } EXPECT 1 TIMEOUT 1 NAME substring at beginning PROG BEGIN { print(strcontains("hello-test-world", "hello")); exit(); } EXPECT 1 TIMEOUT 1 NAME no match PROG BEGIN { print(strcontains("hello-test-world", "not-there")); exit(); } EXPECT 0 TIMEOUT 1 NAME empty haystack, empty needle PROG BEGIN { print(strcontains("", "")); exit(); } EXPECT 1 TIMEOUT 1 NAME non-empty haystack, empty needle PROG BEGIN { print(strcontains("hello", "")); exit(); } EXPECT 1 TIMEOUT 1 NAME empty haystack, non-empty needle PROG BEGIN { print(strcontains("", "test")); exit(); } EXPECT 0 TIMEOUT 1 NAME exact match PROG BEGIN { print(strcontains("hello", "hello")); exit(); } EXPECT 1 TIMEOUT 1 NAME needle longer than haystack PROG BEGIN { print(strcontains("hello", "hello-longer")); exit(); } EXPECT 0 TIMEOUT 1 NAME case sensitivity check PROG BEGIN { print(strcontains("Hello World", "hello")); exit(); } EXPECT 0 TIMEOUT 1 NAME multiple occurrences PROG BEGIN { print(strcontains("test-test-test", "test")); exit(); } EXPECT 1 TIMEOUT 1 NAME overlapping potential matches PROG BEGIN { print(strcontains("ababac", "abac")); exit(); } EXPECT 1 TIMEOUT 1 NAME special characters PROG BEGIN { print(strcontains("hello\tworld", "\two")); exit(); } EXPECT 1 TIMEOUT 1 NAME null terminator inside haystack PROG BEGIN { $str = "hello\0world"; print(strcontains($str, "world")); exit(); } EXPECT 0 TIMEOUT 1 NAME content after null terminator PROG BEGIN { $str = "hello\0test"; print(strcontains($str, "test")); exit(); } EXPECT 0 TIMEOUT 1 NAME null character in needle PROG BEGIN { $needle = "wo\0rld"; print(strcontains("hello world", $needle)); exit(); } EXPECT 1 TIMEOUT 1 NAME null character in both PROG BEGIN { $haystack = "hello\0world"; $needle = "wo\0rld"; print(strcontains($haystack, $needle)); exit(); } EXPECT 0 TIMEOUT 1 bpftrace-0.23.2/tests/runtime/struct000066400000000000000000000034201477746507000175000ustar00rootroot00000000000000NAME struct assignment into variable PROG struct Foo { int m; } u:./testprogs/simple_struct:func { $s = *((struct Foo *)arg0); printf("Result: %d\n", $s.m); exit(); } EXPECT Result: 2 TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct assignment into map PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @s = *((struct Foo *)arg0); exit(); } EXPECT @s: { .m = 2, .n = 3 } TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct field order PROG struct Foo { int n; int m; } u:./testprogs/simple_struct:func { @s = *((struct Foo *)arg0); exit(); } EXPECT @s: { .n = 2, .m = 3 } TIMEOUT 4 AFTER ./testprogs/simple_struct NAME nested struct assignment into map PROG struct Foo { struct { int m[1] } y; struct { int n } a; } u:./testprogs/simple_struct:func { @s = *((struct Foo *)arg0); exit(); } EXPECT @s: { .y = { .m = [2] }, .a = { .n = 3 } } TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct as a map key PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @s[*((struct Foo *)arg0)] = 0; exit(); } EXPECT @s[{ .m = 2, .n = 3 }]: 0 TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct as a part of multikey PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @s[*((struct Foo *)arg0), 42] = 0; exit(); } EXPECT @s[{ .m = 2, .n = 3 }, 42]: 0 TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct as a map key assigned from another map PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @x = *((struct Foo *)arg0); @s[@x] = 0; exit(); } EXPECT @s[{ .m = 2, .n = 3 }]: 0 TIMEOUT 4 AFTER ./testprogs/simple_struct NAME struct in a tuple PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @s = (1, *((struct Foo *)arg0)); exit(); } EXPECT @s: (1, { .m = 2, .n = 3 }) TIMEOUT 4 AFTER ./testprogs/simple_struct bpftrace-0.23.2/tests/runtime/tuples000066400000000000000000000101401477746507000174650ustar00rootroot00000000000000NAME basic tuple PROG BEGIN { $v = 99; $t = (0, 1, "str", (5, 6), $v); printf("%d %d %s %d %d %d\n", $t.0, $t.1, $t.2, $t.3.0, $t.3.1, $t.4); exit(); } EXPECT 0 1 str 5 6 99 NAME basic tuple map PROG BEGIN { $v = 99; @t = (0, 1, "str"); printf("%d %d %s\n", @t.0, @t.1, @t.2); exit(); } EXPECT 0 1 str NAME mixed int tuple map PROG BEGIN { @ = ( (int32) -100, (int8) 10, 50 ); exit();} EXPECT @: (-100, 10, 50) NAME mixed int tuple map 2 PROG BEGIN { @ = ( -100, (int8) 10, (int32) 50 ); exit();} EXPECT @: (-100, 10, 50) NAME mixed int tuple map 3 PROG BEGIN { @ = ( -100, (int8) 10, (int32) 50, 100 ); exit();} EXPECT @: (-100, 10, 50, 100) NAME tuple map key PROG BEGIN { @[(1, "hello")] = 1; exit();} EXPECT @[1, hello]: 1 NAME tuple map key and value PROG BEGIN { @[(1, 2)] = (3, 4); exit();} EXPECT @[1, 2]: (3, 4) NAME implicit conversion of multi map key to tuple PROG BEGIN { @["abcd", 2] = (3, 4, 5); exit();} EXPECT @[abcd, 2]: (3, 4, 5) NAME tuple map key same as assoc array PROG BEGIN { @[(1, "hello")] = 1; @[2, "hello"] = 2; delete(@, (1, "hello")); exit();} EXPECT @[2, hello]: 2 NAME tuple map key variable PROG BEGIN { $a = (1, "hello"); @[(1, "hello")] = 1; @[$a] = 2; exit();} EXPECT @[1, hello]: 2 NAME tuple map key string resize PROG BEGIN { @a["hellotherelongstr", 4] = 1; @a["by", 6] = 2; exit(); } EXPECT @a[hellotherelongstr, 4]: 1 EXPECT @a[by, 6]: 2 NAME tuple map key compatible int sizes PROG BEGIN { $a = (1,(123,(uint64)1234)); $b = (4,(1234,(uint8)123)); @a[$a] = 1; @a[$b] = 2; exit(); } EXPECT @a[1, (123, 1234)]: 1 EXPECT @a[4, (1234, 123)]: 2 NAME complex tuple 1 PROG BEGIN { print(((int8)-100, (int8) 100, "abcdef", 3, (int32) 1, (int64)-10, (int8)10, (int16)-555)); exit(); } EXPECT (-100, 100, abcdef, 3, 1, -10, 10, -555) NAME tuple struct sizing 1 PROG BEGIN { $t = ((int8) 1, (int64) 1, (int8) 1, (int64) 1); print(sizeof($t)); exit() } EXPECT 32 NAME tuple struct sizing 2 PROG BEGIN { $t = ((int8) 1, (int16) 1, (int32) 1); print(sizeof($t)); exit() } EXPECT 8 NAME tuple struct sizing 3 PROG BEGIN { $t = ((int32) 1, (int16) 1, (int8) 1); print(sizeof($t)); exit() } EXPECT 8 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 48 NAME struct in tuple PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { @t = (1, *((struct Foo *)arg0)); exit(); } EXPECT @t: (1, { .m = 2, .n = 3 }) AFTER ./testprogs/simple_struct NAME struct in tuple sizing PROG struct Foo { int m; int n; } u:./testprogs/simple_struct:func { $t = ((int32)1, *((struct Foo *)arg0)); print(sizeof($t)); exit(); } EXPECT 12 AFTER ./testprogs/simple_struct NAME array in tuple PROG struct A { int x[4]; } u:./testprogs/array_access:test_struct { @t = (1, ((struct A *)arg0)->x); exit(); } EXPECT @t: (1, [1,2,3,4]) AFTER ./testprogs/array_access NAME array in tuple sizing PROG struct A { int x[4]; } u:./testprogs/array_access:test_struct { $t = ((int32)1, ((struct A *)arg0)->x); print(sizeof($t)); exit(); } EXPECT 20 AFTER ./testprogs/array_access NAME nested tuple PROG BEGIN{ @ = ((int8)1, ((int8)-20, (int8)30)); exit(); } EXPECT @: (1, (-20, 30)) # Careful with '(' and ')', they are read by the test engine as a regex group, # so make sure to escape them. NAME tuple print PROG BEGIN { @ = (1, 2, "string", (4, 5)); exit(); } EXPECT @: (1, 2, string, (4, 5)) NAME tuple strftime type is packed PROG BEGIN { @ = (nsecs, strftime("%M:%S", nsecs)); exit(); } EXPECT_REGEX ^@: \(\d+, \d+:\d+\)$ NAME bytearray in tuple PROG uprobe:./testprogs/uprobe_test:uprobeFunction1 { @ = ((int8)1, usym(reg("ip")), 10); exit(); } EXPECT_REGEX ^@: \(1, uprobeFunction1, 10\)$ ARCH x86_64 AFTER ./testprogs/uprobe_test NAME bytearray in tuple PROG uprobe:./testprogs/uprobe_test:uprobeFunction1 { @ = ((int8)1, usym(reg("nip")), 10); exit(); } EXPECT_REGEX ^@: \(1, uprobeFunction1, 10\)$ ARCH ppc64|ppc64le AFTER ./testprogs/uprobe_test NAME ustack in tuple PROG BEGIN { print((ustack, "a")); exit(); } EXPECT Attaching 1 probe... NAME kstack in tuple PROG BEGIN { print((kstack, "a")); exit(); } EXPECT Attaching 1 probe... bpftrace-0.23.2/tests/runtime/uprobe000066400000000000000000000061051477746507000174530ustar00rootroot00000000000000NAME uprobes - list probes by file RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe_test:*' EXPECT uprobe:./testprogs/uprobe_test:uprobeFunction1 NAME uprobes - list probes by file with wildcarded filter RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe_test:uprobeFunc*' EXPECT uprobe:./testprogs/uprobe_test:uprobeFunction1 NAME uprobes - list probes with wildcarded file matching multiple files RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe*:*' EXPECT uprobe:./testprogs/uprobe_test:uprobeFunction1 NAME uprobes - list probes by file with wildcarded file and filter RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe_test*:uprobeFunc*' EXPECT uprobe:./testprogs/uprobe_test:uprobeFunction1 NAME uprobes - list probes by file with specific filter RUN {{BPFTRACE}} -l 'uprobe:./testprogs/uprobe_test:uprobeFunction1' EXPECT uprobe:./testprogs/uprobe_test:uprobeFunction1 NAME uprobes - list probes by file with wildcarded probe type RUN {{BPFTRACE}} -l '*:./testprogs/uprobe_test:*' | grep -e '^uprobe:' EXPECT uprobe:./testprogs/uprobe_test:uprobeFunction1 NAME uprobes - list probes by pid RUN {{BPFTRACE}} -l -p {{BEFORE_PID}} | grep -e '^uprobe' EXPECT_REGEX uprobe:.*/testprogs/uprobe_test:uprobeFunction1 BEFORE ./testprogs/uprobe_test NAME uprobes - list probes by pid; uprobes only RUN {{BPFTRACE}} -l 'uprobe:*' -p {{BEFORE_PID}} EXPECT_REGEX uprobe:.*/testprogs/uprobe_test:uprobeFunction1 BEFORE ./testprogs/uprobe_test NAME uprobes - list probes by pid in separate mount namespace RUN {{BPFTRACE}} -l -p {{BEFORE_PID}} | grep -e '^uprobe' EXPECT_REGEX uprobe:.*/bpftrace-unshare-mountns-test/uprobe_test:uprobeFunction1 BEFORE ./testprogs/mountns_wrapper uprobe_test NAME uprobes - attach to probe for executable in separate mount namespace RUN {{BPFTRACE}} -e 'uprobe:/tmp/bpftrace-unshare-mountns-test/uprobe_test:uprobeFunction1 { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT Attaching 1 probe... BEFORE ./testprogs/mountns_wrapper uprobe_test NAME uprobes - attach to probe for executable in a pivot_root'd mount namespace RUN {{BPFTRACE}} -e 'uprobe:/proc/{{BEFORE_PID}}/root/uprobe_test:uprobeFunction1 { printf("func %s\n", func); exit(); }' EXPECT func uprobeFunction1 BEFORE ./testprogs/mountns_pivot_wrapper uprobe_test NAME uprobes - attach to probe by pid with only wildcard RUN {{BPFTRACE}} -e 'uprobe:*:uprobeFunction1 { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT Attaching 1 probe... BEFORE ./testprogs/uprobe_test NAME uprobes - attach to multiple probes by pid with only wildcard RUN {{BPFTRACE}} -e 'uprobe:*:uprobeFunc* { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT here BEFORE ./testprogs/uprobe_test NAME uprobes - list probes in non-executable library RUN {{BPFTRACE}} -l 'uprobe:./testlibs/libsimple.so:fun' EXPECT uprobe:./testlibs/libsimple.so:fun NAME uprobes - probe function in non-executable library PROG uprobe:./testlibs/libsimple.so:fun {} EXPECT Attaching 1 probe... NAME uprobes - attach to single process with pid arg RUN {{BPFTRACE}} runtime/scripts/uprobe_pid_check.bt -p {{BEFORE_PID}} {{BEFORE_PID}} EXPECT hello BEFORE ./testprogs/uprobe_fork_loop bpftrace-0.23.2/tests/runtime/usdt000066400000000000000000000320311477746507000171330ustar00rootroot00000000000000NAME usdt probes - list probes by file RUN {{BPFTRACE}} -l 'usdt:./testprogs/usdt_test:*' EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt - list probes by file with wildcarded probe type RUN {{BPFTRACE}} -l '*:./testprogs/usdt_test:*' | grep -e '^usdt:' EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe NAME usdt probes - list probes by pid RUN {{BPFTRACE}} -l -p {{BEFORE_PID}} | grep -e '^usdt:' EXPECT_REGEX ^usdt:.*/testprogs/usdt_test:tracetest:testprobe$ BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - list probes by pid; usdt only RUN {{BPFTRACE}} -l 'usdt:*' -p {{BEFORE_PID}} EXPECT_REGEX ^usdt:.*/testprogs/usdt_test:tracetest:testprobe$ BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - lists linked library probes by pid RUN {{BPFTRACE}} -l 'usdt:*' -p $(pidof usdt_lib) EXPECT_REGEX usdt:.*/libusdt_tp.so:tracetestlib:lib_probe_1$ BEFORE ./testprogs/usdt_lib REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - filter probes by file on provider RUN {{BPFTRACE}} -l 'usdt:./testprogs/usdt_test:tracetest2:*' EXPECT usdt:./testprogs/usdt_test:tracetest2:testprobe2 REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - filter probes by pid on provider RUN {{BPFTRACE}} -l 'usdt:*:tracetest2:*' -p {{BEFORE_PID}} EXPECT_REGEX ^usdt:.*/usdt_test:tracetest2:testprobe2$ BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - filter probes by wildcard file and wildcard probe name RUN {{BPFTRACE}} -l 'usdt:./testprogs/usdt_test*:tracetest:test*' EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe2 REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - filter probes by file and wildcard probe name RUN {{BPFTRACE}} -l 'usdt:./testprogs/usdt_test:tracetest:test*' EXPECT usdt:./testprogs/usdt_test:tracetest:testprobe2 REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - filter probes by pid and wildcard probe name RUN {{BPFTRACE}} -l 'usdt:*:tracetest:test*' -p {{BEFORE_PID}} EXPECT_REGEX ^usdt:.*/usdt_test:tracetest:testprobe$ EXPECT_REGEX ^usdt:.*/usdt_test:tracetest:testprobe2$ BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to fully specified probe by file PROG usdt:./testprogs/usdt_test:tracetest:testprobe { printf("here\n" ); exit(); } EXPECT here BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to fully specified probe of child RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:tracetest:testprobe { printf("here\n" ); exit(); }' -c ./testprogs/usdt_test EXPECT here NAME usdt probes - attach to fully specified probe by pid RUN {{BPFTRACE}} -e 'usdt::tracetest:testprobe { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT here BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to fully specified probe all pids RUN {{BPFTRACE}} -e 'usdt:*:tracetest:testprobe { printf("here\n" ); exit(); }' EXPECT here BEFORE ./testprogs/usdt_test NEW_PIDNS # On 9pfs, is_elf() hangs on a syscall to fstat64. We suspect it's # a bug with 9pfs. https://github.com/danobi/vmtest/pull/88 should help # when it lands. So for now, don't run this test on 9pfs. REQUIRES findmnt -n / | awk '{exit $3!="9pfs"}' NAME usdt probes - attach to fully specified library probe by pid RUN {{BPFTRACE}} -e 'usdt:./testlibs/libusdt_tp.so:tracetestlib:lib_probe_1 { printf("here\n" ); exit(); }' -p $(pidof usdt_lib) BEFORE ./testprogs/usdt_lib EXPECT here REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - all probes by wildcard and file PROG usdt:./testprogs/usdt_test:* { printf("here\n" ); exit(); } EXPECT Attaching 3 probes... BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - all probes by wildcard and file with child RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:* { printf("here\n" ); exit(); }' -c ./testprogs/usdt_test EXPECT Attaching 3 probes... # TODO(mmarchini): re-enable this test # This test has two problems: it relies on the latest version of bcc and it # assumes USDTs are coming only from the binary. On Ubuntu, glibc is built with # USDT support, which means it will attach to 43 probes instead of 3. Before # re-enabling this test, we should: # - Skip if bcc doesn't support multiple probes with the same name # - Fix https://github.com/bpftrace/bpftrace/issues/565#issuecomment-496731112 # and https://github.com/bpftrace/bpftrace/issues/688 NAME usdt probes - all probes by wildcard and pid RUN {{BPFTRACE}} -e 'usdt:* { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT Attaching 3 probes... BEFORE ./testprogs/usdt_test REQUIRES bash -c "exit 1" # REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to probe by wildcard and file PROG usdt:./testprogs/usdt_test::*probe2 { printf("here\n" ); exit(); } EXPECT Attaching 2 probes... BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to probe by wildcard and file with child RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test::*probe2 { printf("here\n" ); exit(); }' -c ./testprogs/usdt_test EXPECT Attaching 2 probes... NAME usdt probes - attach to probes by wildcard file PROG usdt:./testprogs/usdt_test*::* { printf("here\n" ); exit(); } EXPECT Attaching 3 probes... BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to probes by wildcard file with child RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test*::* { printf("here\n" ); exit(); }' -c ./testprogs/usdt_test EXPECT Attaching 3 probes... NAME usdt probes - attach to probe on multiple files by wildcard PROG usdt:./testprogs/usdt*::* { printf("here\n" ); exit(); } EXPECT Attaching 48 probes... NAME usdt probes - attach to probe on multiple providers by wildcard and pid RUN {{BPFTRACE}} -e 'usdt:::*probe2 { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT Attaching 2 probes... BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to multiple probes with different number of locations by wildcard PROG usdt:./testprogs/usdt_multiple_locations:tracetest:testprobe* { printf("here\n" ); exit(); } BEFORE ./testprogs/usdt_multiple_locations EXPECT Attaching 6 probes... REQUIRES ./testprogs/systemtap_sys_sdt_check # TODO(mmarchini): re-enable this test # This test relies on the latest version of bcc. Before re-enabling this test, # we should: # - Skip if bcc doesn't support multiple probes with the same name NAME usdt probes - attach to probe with args by file PROG usdt:./testprogs/usdt_test:* { printf("%s\n", str(arg1) ); exit(); } EXPECT Hello world BEFORE ./testprogs/usdt_test REQUIRES bash -c "exit 1" # REQUIRES ./testprogs/systemtap_sys_sdt_check # TODO(mmarchini): re-enable this test # This test relies on the latest version of bcc. Before re-enabling this test, # we should: # - Skip if bcc doesn't support multiple probes with the same name NAME usdt probes - attach to probe with args by pid RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:* { printf("%s\n", str(arg1) ); exit(); }' -p {{BEFORE_PID}} EXPECT Hello world BEFORE ./testprogs/usdt_test REQUIRES bash -c "exit 1" # REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to probe with probe builtin and args by file PROG usdt:./testprogs/usdt_test:* { printf("%lld %s\n", arg0, probe ); exit(); } EXPECT_REGEX ^\d+ usdt:./testprogs/usdt_test:tracetest.?:testprobe.?$ BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to probe with probe builtin and args by file with child RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:* { printf("%lld %s\n", arg0, probe ); exit(); }' -c ./testprogs/usdt_test EXPECT_REGEX ^\d+ usdt:./testprogs/usdt_test:tracetest.?:testprobe.?$ NAME usdt probes - attach to probe with probe builtin and args by pid RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_test:* { printf("%lld %s\n", arg0, probe ); exit(); }' -p {{BEFORE_PID}} EXPECT_REGEX ^\d+ usdt:.*/testprogs/usdt_test:tracetest.?:testprobe.?$ BEFORE ./testprogs/usdt_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - attach to probe with semaphore RUN {{BPFTRACE}} -e 'usdt::tracetest:testprobe { printf("%s\n", str(arg1) ); exit(); }' -p {{BEFORE_PID}} EXPECT tracetest_testprobe_semaphore: 1 BEFORE ./testprogs/usdt_semaphore_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - file based semaphore activation RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { printf("%s\n", str(arg1) ); exit(); }' --usdt-file-activation EXPECT tracetest_testprobe_semaphore: 1 BEFORE ./testprogs/usdt_semaphore_test REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes - file based semaphore activation no process RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { exit(); }' --usdt-file-activation EXPECT Failed to find processes running REQUIRES ./testprogs/systemtap_sys_sdt_check 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 NAME usdt probes - file based semaphore activation no process, kernel usdt semaphore RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { exit(); }' --usdt-file-activation EXPECT Attaching 1 probe... REQUIRES ./testprogs/systemtap_sys_sdt_check REQUIRES_FEATURE uprobe_refcount # We should be able to activate a semaphore without specifying a PID or # --usdt-file-activation if the kernel handles the semaphore NAME usdt probes - file based semaphore activation PROG usdt:./testprogs/usdt_semaphore_test:tracetest:testprobe { printf("%s\n", str(arg1) ); exit(); } EXPECT tracetest_testprobe_semaphore: 1 BEFORE ./testprogs/usdt_semaphore_test REQUIRES ./testprogs/systemtap_sys_sdt_check REQUIRES_FEATURE uprobe_refcount NAME usdt probes - file based semaphore activation multi process RUN {{BPFTRACE}} runtime/scripts/usdt_file_activation_multiprocess.bt --usdt-file-activation EXPECT found 2 processes BEFORE ./testprogs/usdt_semaphore_test BEFORE ./testprogs/usdt_semaphore_test REQUIRES ./testprogs/systemtap_sys_sdt_check # See https://github.com/bpftrace/bpftrace/pull/2438 SKIP_IF_ENV_HAS CI=true NAME usdt probes - list probes by pid in separate mountns RUN {{BPFTRACE}} -l 'usdt:*' -p {{BEFORE_PID}} EXPECT_REGEX ^usdt:.*/tmp/bpftrace-unshare-mountns-test/usdt_test:tracetest:testprobe$ BEFORE ./testprogs/mountns_wrapper usdt_test # TODO(dalehamel): re-enable this test # This test relies on the latest version of bcc (expected to be released as 0.13.0) # Once we build against this version with USDT fixes, we should re-enable this test. NAME usdt probes - attach to fully specified probe by pid in separate mountns RUN {{BPFTRACE}} -e 'usdt:/tmp/bpftrace-unshare-mountns-test/usdt_test:tracetest:testprobe { printf("here\n" ); exit(); }' -p {{BEFORE_PID}} EXPECT here BEFORE ./testprogs/mountns_wrapper usdt_test REQUIRES bash -c "exit 1" NAME usdt quoted probe name RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_quoted_probe:test:"\"probe1\"" { printf("%d\n", arg0); exit(); }' -p {{BEFORE_PID}} EXPECT 1 BEFORE ./testprogs/usdt_quoted_probe REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt sized arguments RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_sized_args:test:probe2 { printf("%ld\n", arg0); exit(); }' -p {{BEFORE_PID}} EXPECT 1 BEFORE ./testprogs/usdt_sized_args NAME usdt constant arguments RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_args:usdt_args:const_* { printf("%lld ", arg0); }' -c ./testprogs/usdt_args EXPECT -579005069656919568 -579005069656919568 4092785136 -202182160 61936 -3600 240 -16 REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt reg arguments RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_args:usdt_args:reg_* { printf("%lld ", arg0); }' -c ./testprogs/usdt_args EXPECT -579005069656919568 -579005069656919568 4092785136 -202182160 61936 -3600 240 -16 REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt addr arguments RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_args:usdt_args:addr_* { printf("%lld ", arg0); }' -c ./testprogs/usdt_args EXPECT -579005069656919568 -579005069656919568 4092785136 -202182160 61936 -3600 240 -16 REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt addr+index arguments RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_args:usdt_args:index_* { printf("%lld ", arg0); }' -c ./testprogs/usdt_args EXPECT -579005069656919568 -579005069656919568 4092785136 -202182160 61936 -3600 240 -16 REQUIRES ./testprogs/systemtap_sys_sdt_check # USDT probes can be inlined which creates duplicate identical probes. We must # attach to all of them NAME usdt duplicated markers RUN {{BPFTRACE}} -e 'usdt:./testprogs/usdt_inlined:tracetest:testprobe { printf("%d\n", arg1); @a += 1; if (@a >= 2) {exit();} }' -c ./testprogs/usdt_inlined EXPECT_REGEX 999\n100 TIMEOUT 1 REQUIRES ./testprogs/systemtap_sys_sdt_check NAME usdt probes in multiple modules RUN {{BPFTRACE}} runtime/scripts/usdt_multi_modules.bt EXPECT Attaching 2 probes... TIMEOUT 1 REQUIRES ./testprogs/systemtap_sys_sdt_check bpftrace-0.23.2/tests/runtime/variable000066400000000000000000000101651477746507000177450ustar00rootroot00000000000000NAME global_int PROG i:ms:1 {@a = 10; printf("%d\n", @a); exit();} EXPECT @a: 10 NAME global_string PROG i:ms:1 {@a = "hi"; printf("%s\n", @a); exit();} EXPECT @a: hi NAME global_buf PROG i:ms:1 {@a = buf("hi", 2); printf("%r\n", @a); exit();} EXPECT @a: hi NAME local_int PROG i:ms:1 {$a = 10; printf("a=%d\n", $a); exit();} EXPECT a=10 NAME local_string PROG i:ms:1 {$a = "hi"; printf("a=%s\n", $a); exit();} EXPECT a=hi NAME local_buf PROG i:ms:1 {$a = buf("hi", 2); printf("a=%r\n", $a); exit();} EXPECT a=hi NAME buf_equality PROG i:ms:1 {$a = buf("hi", 2); $b = buf("bye", 3); printf("equal=%d, unequal=%d\n", $a == $a, $a != $b); exit();} EXPECT equal=1, unequal=1 NAME global_associative_arrays PROG BEGIN { @map[123] = 456; } END { printf("val: %d\n", @map[123]); exit(); } EXPECT val: 456 NAME scratch PROG BEGIN { @map[123] = 456; } END { $val = @map[123]; printf("val: %d\n", $val); exit(); } EXPECT val: 456 NAME 32-bit tracepoint arg PROG tracepoint:syscalls:sys_enter_openat /comm == "syscall"/ { $i = args.flags; printf("openflags: %d\n", $i); if ($i == 64) { exit() } } EXPECT openflags: 64 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_REGEX @: [1-9][0-9]* 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 map tuple string resize PROG BEGIN { @[1] = ("hi", 1); @[1] = ("hellolongstr", 2); @[1] = ("by", 3); exit(); } EXPECT @[1]: (by, 3) TIMEOUT 2 NAME map key tuple string resize PROG BEGIN { @["hi", 1] = 1; @["hellolongstr", 2] = 2; @["by", 3] = 3; exit(); } EXPECT @[hi, 1]: 1 EXPECT @[hellolongstr, 2]: 2 EXPECT @[by, 3]: 3 TIMEOUT 2 NAME variable tuple string resize PROG BEGIN { $a = ("hi", 1); print(($a)); $a = ("hellolongstr", 2); print(($a)); $a = ("by", 3); print(($a)); exit(); } EXPECT (hi, 1) EXPECT (hellolongstr, 2) EXPECT (by, 3) TIMEOUT 2 NAME variable nested tuple string resize PROG BEGIN { $a = ("hi", ("hellolongstr", 2)); print(($a)); $a = ("hellolongstr", ("hi", 5)); print(($a)); exit(); } EXPECT (hi, (hellolongstr, 2)) EXPECT (hellolongstr, (hi, 5)) TIMEOUT 2 NAME map nested tuple string resize PROG BEGIN { @[1] = ("hi", ("hellolongstr", 2)); @[1] = ("hellolongstr", ("hi", 5)); exit(); } EXPECT @[1]: (hellolongstr, (hi, 5)) TIMEOUT 2 NAME map key nested tuple string resize PROG BEGIN { @["hi", ("hellolongstr", 2)] = 1; @["hellolongstr", ("hi", 5)] = 2; exit(); } EXPECT @[hi, (hellolongstr, 2)]: 1 EXPECT @[hellolongstr, (hi, 5)]: 2 TIMEOUT 2 NAME map key tuple with casted ints PROG BEGIN { @a[(int16)-1, ((int32)-2, 3)] = 10; @a[5, (6, 7)] = 11; $c = ((int8)-4, ((int16)-5, -6)); @a[$c] = 12; exit(); } EXPECT @a[-4, (-5, -6)]: 12 EXPECT @a[-1, (-2, 3)]: 10 EXPECT @a[5, (6, 7)]: 11 TIMEOUT 2 NAME variable declaration PROG BEGIN { let $a; $a = 10; printf("a=%d\n", $a); exit();} EXPECT a=10 NAME variable declaration with builtin PROG BEGIN { let $f: struct task_struct *; $f = curtask; print($f->pid); exit();} EXPECT_REGEX [0-9]+ NAME variable declaration with unresolved type PROG BEGIN { let $x: struct Foo[1]; exit(); } EXPECT Attaching 1 probe... NAME variable declaration not initialized PROG BEGIN { let $a; if (false) { $a = 1; } @b = $a; exit();} EXPECT @b: 0 NAME variable doesn't escape scope RUN {{BPFTRACE}} runtime/scripts/variable_scope.bt EXPECT 0 NAME late variable declaration RUN {{BPFTRACE}} runtime/scripts/variable_scope_late_decl.bt EXPECT (a, 1) EXPECT (b, 2) EXPECT (c, 0) NAME late map evaluation with ctx capture RUN {{BPFTRACE}} -e 'interval:ms:100 { $x = 1; for ($kv : @a) { print(($x, $kv.0, $kv.1)); exit(); } @a[1] = 1; }' EXPECT (1, 1, 1) bpftrace-0.23.2/tests/runtime/watchpoint000066400000000000000000000043501477746507000203370ustar00rootroot00000000000000NAME watchpoint - absolute address BEFORE ./testprogs/watchpoint RUN {{BPFTRACE}} -e "watchpoint:$(awk '{print $1}' /tmp/watchpoint_mem):8:w { printf(\"hit!\n\"); exit() }" -p {{BEFORE_PID}} EXPECT hit! ARCH aarch64|ppc64|ppc64le|x86_64 CLEANUP rm -f /tmp/watchpoint_mem NAME kwatchpoint - knl absolute address RUN {{BPFTRACE}} -e "watchpoint:0x$(awk '$3 == "jiffies" {print $1}' /proc/kallsyms):4:w { printf(\"hit\n\"); exit(); }" EXPECT hit ARCH aarch64|ppc64|ppc64le|x86_64 REQUIRES awk '$3 == "jiffies" {got=1} END {exit !got}' /proc/kallsyms NAME function_arg_addr RUN {{BPFTRACE}} -e 'watchpoint:increment+arg1:4:w { printf("hit!\n"); exit() }' -c ./testprogs/watchpoint_func EXPECT hit! ARCH aarch64|x86_64 REQUIRES_FEATURE signal NAME async_function_arg_addr RUN {{BPFTRACE}} -e 'asyncwatchpoint:increment+arg1:4:w { printf("hit!\n"); exit() }' -c ./testprogs/watchpoint_func EXPECT hit! ARCH aarch64|x86_64 REQUIRES_FEATURE signal NAME function_arg_addr_process_flag RUN {{BPFTRACE}} -e 'watchpoint:increment+arg1:4:w { printf("hit!\n"); exit() }' -p $(pidof watchpoint_func) BEFORE ./testprogs/watchpoint_func EXPECT hit! ARCH aarch64|x86_64 REQUIRES_FEATURE signal NAME many_function_probes RUN {{BPFTRACE}} -e 'watchpoint:increment+arg0:4:w { printf("hit!\n") }' -c ./testprogs/watchpoint_func_many_probes EXPECT Failed to attach watchpoint probe. You are out of watchpoint registers. ARCH aarch64|x86_64 REQUIRES_FEATURE signal NAME unwatch RUN {{BPFTRACE}} runtime/scripts/watchpoint_unwatch.bt -c ./testprogs/watchpoint_unwatch EXPECT count=1 ARCH aarch64|x86_64 REQUIRES_FEATURE signal NAME function_multiattach RUN {{BPFTRACE}} runtime/scripts/watchpoint_multiattach.bt -c ./testprogs/watchpoint_func_wildcard EXPECT count=3 ARCH aarch64|x86_64 REQUIRES_FEATURE signal NAME wildcarded_function RUN {{BPFTRACE}} -e 'watchpoint:increment_*+arg0:4:w { printf("hit!\n") }' -c ./testprogs/watchpoint_func_wildcard EXPECT Failed to attach watchpoint probe. You are out of watchpoint registers. ARCH aarch64|x86_64 REQUIRES_FEATURE signal NAME unique_probe_bodies RUN {{BPFTRACE}} -e 'watchpoint:increment_*+arg0:4:w { printf("%s!\n", probe) }' -c ./testprogs/watchpoint_func_wildcard EXPECT_REGEX .*increment_0:4:w! ARCH aarch64|x86_64 REQUIRES_FEATURE signal bpftrace-0.23.2/tests/scopeguard.cpp000066400000000000000000000015231477746507000174100ustar00rootroot00000000000000#include #include #include "scopeguard.h" #include namespace bpftrace { TEST(ScopeGuardTest, InnerScope) { int x = 5; { SCOPE_EXIT { x++; }; } EXPECT_EQ(x, 6); } TEST(ScopeGuardTest, FunctionReturn) { int x = 5; [&]() { SCOPE_EXIT { x++; }; x++; return; }(); EXPECT_EQ(x, 7); } TEST(ScopeGuardTest, ExceptionContext) { int x = 5; try { [&]() { SCOPE_EXIT { x++; }; throw std::runtime_error("exception"); }(); } catch (const std::exception &) { EXPECT_EQ(x, 6); x++; } EXPECT_EQ(x, 7); } TEST(ScopeGuardTest, MultipleGuards) { int x = 5; { SCOPE_EXIT { x++; }; SCOPE_EXIT { x++; }; } EXPECT_EQ(x, 7); } } // namespace bpftrace bpftrace-0.23.2/tests/semantic_analyser.cpp000066400000000000000000005056231477746507000207670ustar00rootroot00000000000000#include "ast/passes/semantic_analyser.h" #include "ast/passes/field_analyser.h" #include "ast/passes/printer.h" #include "bpffeature.h" #include "bpftrace.h" #include "clang_parser.h" #include "driver.h" #include "mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace bpftrace::test::semantic_analyser { #include "btf_common.h" using ::testing::_; using ::testing::HasSubstr; void test_for_warning(BPFtrace &bpftrace, const std::string &input, const std::string &warning, bool invert = false, bool safe_mode = true) { Driver driver(bpftrace); bpftrace.safe_mode_ = safe_mode; ASSERT_EQ(driver.parse_str(input), 0); ClangParser clang; clang.parse(driver.ctx.root, bpftrace); ASSERT_EQ(driver.parse_str(input), 0); std::stringstream out; ast::SemanticAnalyser semantics(driver.ctx, bpftrace, out); semantics.analyse(); if (invert) EXPECT_THAT(out.str(), Not(HasSubstr(warning))); else EXPECT_THAT(out.str(), HasSubstr(warning)); } void test_for_warning(const std::string &input, const std::string &warning, bool invert = false, bool safe_mode = true) { auto bpftrace = get_mock_bpftrace(); test_for_warning(*bpftrace, input, warning, invert, safe_mode); } void test(BPFtrace &bpftrace, bool mock_has_features, Driver &driver, std::string_view input, int expected_result, std::string_view expected_error = {}, bool safe_mode = true, bool has_child = false) { if (!input.empty() && input[0] == '\n') input.remove_prefix(1); // Remove initial '\n' std::stringstream out; std::stringstream msg; msg << "\nInput:\n" << input << "\n\nOutput:\n"; bpftrace.safe_mode_ = safe_mode; ASSERT_EQ(driver.parse_str(input), 0); ast::FieldAnalyser fields(driver.ctx, bpftrace, out); ASSERT_EQ(fields.analyse(), 0) << msg.str() + out.str(); ClangParser clang; clang.parse(driver.ctx.root, 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.ctx, bpftrace, out, has_child); if (expected_result == -1) { // Accept any failure result EXPECT_NE(0, semantics.analyse()) << msg.str() + out.str(); } else { EXPECT_EQ(expected_result, semantics.analyse()) << msg.str() + out.str(); } if (expected_error.data()) { if (!expected_error.empty() && expected_error[0] == '\n') expected_error.remove_prefix(1); // Remove initial '\n' EXPECT_EQ(expected_error, out.str()); } } void test(BPFtrace &bpftrace, std::string_view input, bool safe_mode = true) { Driver driver(bpftrace); test(bpftrace, true, driver, input, 0, {}, safe_mode, false); } void test(BPFtrace &bpftrace, std::string_view input, int expected_result, bool safe_mode = true) { // This function will eventually be deprecated in favour of test_error() assert(expected_result != 0 && "Use test(BPFtrace&, std::string_view) for expected successes"); Driver driver(bpftrace); test(bpftrace, true, driver, input, expected_result, {}, safe_mode, false); } void test(Driver &driver, std::string_view input) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, true, driver, input, 0, {}, true, false); } void test(Driver &driver, std::string_view input, int expected_result) { // This function will eventually be deprecated in favour of test_error() assert(expected_result != 0 && "Use test(Driver&, std::string_view) for expected successes"); auto bpftrace = get_mock_bpftrace(); test(*bpftrace, true, driver, input, expected_result, {}, true, false); } void test(MockBPFfeature &feature, std::string_view input) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); bool mock_has_features = feature.has_features_; test(*bpftrace, mock_has_features, driver, input, 0, {}, true, false); } void test(MockBPFfeature &feature, std::string_view input, int expected_result, bool safe_mode = true) { // This function will eventually be deprecated in favour of test_error() assert(expected_result != 0 && "Use test(MockBPFfeature&, std::string_view) for expected successes"); auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); bool mock_has_features = feature.has_features_; test(*bpftrace, mock_has_features, driver, input, expected_result, {}, safe_mode, false); } void test(std::string_view input, int expected_result, bool safe_mode, bool has_child = false) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); test(*bpftrace, true, driver, input, expected_result, {}, safe_mode, has_child); } void test(std::string_view input, int expected_result) { // This function will eventually be deprecated in favour of test_error() assert(expected_result != 0 && "Use test(std::string_view) for expected successes"); auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); test(*bpftrace, true, driver, input, expected_result, {}, true, false); } void test(std::string_view input) { auto bpftrace = get_mock_bpftrace(); Driver driver(*bpftrace); test(*bpftrace, true, driver, input, 0, {}, true, false); } void test(BPFtrace &bpftrace, std::string_view input, std::string_view expected_ast) { Driver driver(bpftrace); test(bpftrace, true, driver, input, 0, {}, true, false); if (expected_ast[0] == '\n') expected_ast.remove_prefix(1); // Remove initial '\n' std::ostringstream out; ast::Printer printer(driver.ctx, out); printer.print(); if (expected_ast[0] == '*' && expected_ast[expected_ast.size() - 1] == '*') { // Remove globs from beginning and end expected_ast.remove_prefix(1); expected_ast.remove_suffix(1); EXPECT_THAT(out.str(), HasSubstr(expected_ast)); return; } EXPECT_EQ(expected_ast, out.str()); } void test(std::string_view input, std::string_view expected_ast) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, input, expected_ast); } void test_error(BPFtrace &bpftrace, std::string_view input, std::string_view expected_error, bool has_features = true) { Driver driver(bpftrace); test(bpftrace, has_features, driver, input, -1, expected_error, true, false); } void test_error(std::string_view input, std::string_view expected_error, bool has_features = true) { auto bpftrace = get_mock_bpftrace(); test_error(*bpftrace, input, expected_error, has_features); } TEST(semantic_analyser, builtin_variables) { // Just check that each builtin variable exists. test("kprobe:f { pid }"); test("kprobe:f { tid }"); test("kprobe:f { cgroup }"); test("kprobe:f { uid }"); test("kprobe:f { username }"); test("kprobe:f { gid }"); test("kprobe:f { nsecs }"); test("kprobe:f { elapsed }"); test("kprobe:f { numaid }"); test("kprobe:f { cpu }"); test("kprobe:f { curtask }"); test("kprobe:f { rand }"); test("kprobe:f { ctx }"); test("kprobe:f { comm }"); test("kprobe:f { kstack }"); test("kprobe:f { ustack }"); test("kprobe:f { arg0 }"); test("kprobe:f { sarg0 }"); test("kretprobe:f { retval }"); test("kprobe:f { func }"); test("uprobe:/bin/sh:f { func }"); test("kprobe:f { probe }"); test("tracepoint:a:b { args }"); test("kprobe:f { jiffies }"); test_error("kprobe:f { fake }", R"( stdin:1:12-16: ERROR: Unknown identifier: 'fake' kprobe:f { fake } ~~~~ )"); MockBPFfeature feature(false); test(feature, "k:f { cgroup }", 1); test(feature, "k:f { jiffies }", 1); } #ifdef HAVE_LIBLLDB TEST(semantic_analyser, builtin_variables_inline) { auto bpftrace = get_mock_bpftrace(); ConfigSetter configs{ bpftrace->config_, ConfigSource::script }; configs.set(ConfigKeyBool::probe_inline, true); // Check argument builtins are rejected when `probe_inline` is enabled. test_error(*bpftrace, "uprobe:/bin/sh:f { arg0 }", R"( stdin:1:20-24: ERROR: The arg0 builtin can only be used when the probe_inline config is disabled. uprobe:/bin/sh:f { arg0 } ~~~~ )"); test_error(*bpftrace, "uprobe:/bin/sh:f { sarg0 }", R"( stdin:1:20-25: ERROR: The sarg0 builtin can only be used when the probe_inline config is disabled. uprobe:/bin/sh:f { sarg0 } ~~~~~ )"); test_error(*bpftrace, "uprobe:/bin/sh:f { args }", R"( stdin:1:20-24: ERROR: The args builtin can only be used when the probe_inline config is disabled. uprobe:/bin/sh:f { args } ~~~~ stdin:1:20-24: ERROR: Cannot read function parameters uprobe:/bin/sh:f { args } ~~~~ )"); } #endif // HAVE_LIBLLDB TEST(semantic_analyser, builtin_cpid) { test(R"(i:ms:100 { printf("%d\n", cpid); })", 1, false, false); test("i:ms:100 { @=cpid }", 1, false, false); test("i:ms:100 { $a=cpid }", 1, false, false); test(R"(i:ms:100 { printf("%d\n", cpid); })", 0, false, true); test("i:ms:100 { @=cpid }", 0, false, true); test("i:ms:100 { $a=cpid }", 0, false, true); } TEST(semantic_analyser, builtin_functions) { // Just check that each function exists. // Each function should also get its own test case for more thorough testing test("kprobe:f { @x = hist(123) }"); test("kprobe:f { @x = lhist(123, 0, 123, 1) }"); test("kprobe:f { @x = count() }"); test("kprobe:f { @x = sum(pid) }"); test("kprobe:f { @x = min(pid) }"); test("kprobe:f { @x = max(pid) }"); test("kprobe:f { @x = avg(pid) }"); test("kprobe:f { @x = stats(pid) }"); test("kprobe:f { @x = 1; delete(@x) }"); test("kprobe:f { @x = 1; print(@x) }"); test("kprobe:f { @x = 1; clear(@x) }"); test("kprobe:f { @x = 1; zero(@x) }"); test("kprobe:f { @x[1] = 1; if (has_key(@x, 1)) {} }"); test("kprobe:f { @x = 1; @s = len(@x) }"); test("kprobe:f { time() }"); test("kprobe:f { exit() }"); test("kprobe:f { str(0xffff) }"); test("kprobe:f { buf(0xffff, 1) }"); test(R"(kprobe:f { printf("hello\n") })"); test(R"(kprobe:f { system("ls\n") })", 0, false /* safe_node */); test("kprobe:f { join(0) }"); test("kprobe:f { ksym(0xffff) }"); test("kprobe:f { usym(0xffff) }"); test("kprobe:f { kaddr(\"sym\") }"); test("kprobe:f { ntop(0xffff) }"); test("kprobe:f { ntop(2, 0xffff) }"); test("kprobe:f { pton(\"127.0.0.1\") }"); test("kprobe:f { pton(\"::1\") }"); test("kprobe:f { pton(\"0000:0000:0000:0000:0000:0000:0000:0001\") }"); #ifdef __x86_64__ test("kprobe:f { reg(\"ip\") }"); #endif test("kprobe:f { kstack(1) }"); test("kprobe:f { ustack(1) }"); test("kprobe:f { cat(\"/proc/uptime\") }"); test("uprobe:/bin/sh:main { uaddr(\"glob_asciirange\") }"); test("kprobe:f { cgroupid(\"/sys/fs/cgroup/unified/mycg\"); }"); test("kprobe:f { macaddr(0xffff) }"); test("kprobe:f { nsecs() }"); } TEST(semantic_analyser, undefined_map) { test("kprobe:f / @mymap == 123 / { @mymap = 0 }"); test_error("kprobe:f / @mymap == 123 / { 456; }", R"( stdin:1:12-18: ERROR: Undefined map: @mymap kprobe:f / @mymap == 123 / { 456; } ~~~~~~ )"); test_error("kprobe:f / @mymap1 == 1234 / { 1234; @mymap1 = @mymap2; }", R"( stdin:1:48-55: ERROR: Undefined map: @mymap2 kprobe:f / @mymap1 == 1234 / { 1234; @mymap1 = @mymap2; } ~~~~~~~ )"); } TEST(semantic_analyser, consistent_map_values) { test("kprobe:f { @x = 0; @x = 1; }"); test( R"(BEGIN { $a = (3, "hello"); @m[1] = $a; $a = (1,"aaaaaaaaaa"); @m[2] = $a; })"); test_error("kprobe:f { @x = 0; @x = \"a\"; }", R"( stdin:1:20-22: ERROR: Type mismatch for @x: trying to assign value of type 'string[2]' when map already contains a value of type 'int64' kprobe:f { @x = 0; @x = "a"; } ~~ )"); test_error("kprobe:f { @x = 0; @x = *curtask; }", R"( stdin:1:20-22: ERROR: Type mismatch for @x: trying to assign value of type 'struct task_struct' when map already contains a value of type 'int64' kprobe:f { @x = 0; @x = *curtask; } ~~ )"); } TEST(semantic_analyser, consistent_map_keys) { test("BEGIN { @x = 0; @x; }"); test("BEGIN { @x[1] = 0; @x[2]; }"); test("BEGIN { @x[@y] = 5; @y = 1;}"); test("BEGIN { @x[@y[@z]] = 5; @y[2] = 1; @z = @x[0]; }"); test_error("BEGIN { @x = 0; @x[1]; }", R"( stdin:1:17-22: ERROR: Argument mismatch for @x: trying to access with arguments: 'int64' when map expects no arguments BEGIN { @x = 0; @x[1]; } ~~~~~ )"); test_error("BEGIN { @x[1] = 0; @x; }", R"( stdin:1:20-22: ERROR: Argument mismatch for @x: trying to access with no arguments when map expects arguments: 'int64' BEGIN { @x[1] = 0; @x; } ~~ )"); test("BEGIN { @x[1,2] = 0; @x[3,4]; }"); test("BEGIN { @x[1, 1] = 0; @x[(3, 4)]; }"); test("BEGIN { @x[1, ((int8)2, ((int16)3, 4))] = 0; @x[5, (6, (7, 8))]; }"); test_error("BEGIN { @x[1,2] = 0; @x[3]; }", R"( stdin:1:22-27: ERROR: Argument mismatch for @x: trying to access with arguments: 'int64' when map expects arguments: '(int64,int64)' BEGIN { @x[1,2] = 0; @x[3]; } ~~~~~ )"); test_error("BEGIN { @x[1] = 0; @x[2,3]; }", R"( stdin:1:20-27: ERROR: Argument mismatch for @x: trying to access with arguments: '(int64,int64)' when map expects arguments: 'int64' BEGIN { @x[1] = 0; @x[2,3]; } ~~~~~~~ )"); test(R"(BEGIN { @x[1,"a",kstack] = 0; @x[2,"b", kstack]; })"); test_error(R"( BEGIN { @x[1,"a",kstack] = 0; @x["b", 2, kstack]; })", R"( stdin:3:7-25: ERROR: Argument mismatch for @x: trying to access with arguments: '(string[2],int64,kstack)' when map expects arguments: '(int64,string[2],kstack)' @x["b", 2, kstack]; ~~~~~~~~~~~~~~~~~~ )"); test("BEGIN { @map[1, 2] = 1; for ($kv : @map) { @map[$kv.0] = 2; } }"); test_error( R"(BEGIN { @map[1, 2] = 1; for ($kv : @map) { @map[$kv.0.0] = 2; } })", R"( stdin:1:45-58: ERROR: Argument mismatch for @map: trying to access with arguments: 'int64' when map expects arguments: '(int64,int64)' BEGIN { @map[1, 2] = 1; for ($kv : @map) { @map[$kv.0.0] = 2; } } ~~~~~~~~~~~~~ )"); test(R"(BEGIN { $a = (3, "hi"); @map[1, "by"] = 1; @map[$a] = 2; })"); test(R"(BEGIN { @map[1, "hellohello"] = 1; @map[(3, "hi")] = 2; })"); test(R"(BEGIN { $a = (3, "hi"); @map[1, "hellohello"] = 1; @map[$a] = 2; })"); test( R"(BEGIN { $a = (3, "hello"); @m[$a] = 1; $a = (1,"aaaaaaaaaa"); @m[$a] = 2; })"); test( R"(BEGIN { $a = (3, "hi", 50); $b = "goodbye"; $c = (4, $b, 60); @map[$a] = 1; @map[$c] = 2; })"); test( R"(BEGIN { @["hi", ("hellolongstr", 2)] = 1; @["hellolongstr", ("hi", 5)] = 2; })"); test( R"(BEGIN { $a = (3, (uint64)1234); $b = (4, (uint8)5); @map[$a] = 1; @map[$b] = 2; })"); test( R"(BEGIN { $a = (3, (uint8)5); $b = (4, (uint64)1234); @map[$a] = 1; @map[$b] = 2; })"); } TEST(semantic_analyser, if_statements) { test("kprobe:f { if(1) { 123 } }"); test("kprobe:f { if(1) { 123 } else { 456 } }"); test("kprobe:f { if(0) { 123 } else if(1) { 456 } else { 789 } }"); test("kprobe:f { if((int32)pid) { 123 } }"); test("kprobe:f { if(curtask) { 123 } }"); test("kprobe:f { if(curtask && (int32)pid) { 123 } }"); } TEST(semantic_analyser, predicate_expressions) { test("kprobe:f / 999 / { 123 }"); test_error("kprobe:f / \"str\" / { 123 }", R"( stdin:1:10-19: ERROR: Invalid type for predicate: string kprobe:f / "str" / { 123 } ~~~~~~~~~ )"); test_error("kprobe:f / kstack / { 123 }", R"( stdin:1:10-20: ERROR: Invalid type for predicate: kstack kprobe:f / kstack / { 123 } ~~~~~~~~~~ )"); test_error("kprobe:f / @mymap / { @mymap = \"str\" }", R"( stdin:1:10-20: ERROR: Invalid type for predicate: string kprobe:f / @mymap / { @mymap = "str" } ~~~~~~~~~~ )"); } TEST(semantic_analyser, ternary_expressions) { // There are some supported types left out of this list // as they don't make sense or cause other errors e.g. // map aggregate functions and builtins std::unordered_map supported_types = { { "1", "2" }, { "\"lo\"", "\"high\"" }, { "(\"hi\", 1)", "(\"bye\", 2)" }, { "printf(\"lo\")", "exit()" }, { "buf(\"mystr\", 5)", "buf(\"mystr\", 4)" }, { "macaddr(arg0)", "macaddr(arg1)" }, { "kstack(3)", "kstack(3)" }, { "ustack(3)", "ustack(3)" }, { "ntop(arg0)", "ntop(arg1)" }, { "nsecs(boot)", "nsecs(monotonic)" }, { "ksym(arg0)", "ksym(arg1)" }, { "usym(arg0)", "usym(arg1)" }, { "cgroup_path(1)", "cgroup_path(2)" }, { "strerror(1)", "strerror(2)" }, }; for (const auto &[left, right] : supported_types) { test("kprobe:f { curtask ? " + left + " : " + right + " }"); } test("kprobe:f { pid < 10000 ? printf(\"lo\") : exit() }"); test(R"(kprobe:f { @x = pid < 10000 ? printf("lo") : cat("/proc/uptime") })", 3); test("struct Foo { int x; } kprobe:f { curtask ? (struct Foo)*arg0 : (struct " "Foo)*arg1 }", 1); test("struct Foo { int x; } kprobe:f { curtask ? (struct Foo*)arg0 : (struct " "Foo*)arg1 }"); test( R"(kprobe:f { pid < 10000 ? ("a", "hellolongstr") : ("hellolongstr", "b") })"); test( R"(kprobe:f { pid < 10000 ? ("a", "hellolongstr") : ("hellolongstr", "b") })", R"( Program kprobe:f ?: :: [(string[13],string[13])] < :: [uint64] builtin: pid :: [uint32] int: 10000 :: [int64] tuple: :: [(string[2],string[13])] string: a :: [string[2], AS(kernel)] string: hellolongstr :: [string[13], AS(kernel)] tuple: :: [(string[13],string[2])] string: hellolongstr :: [string[13], AS(kernel)] string: b :: [string[2], AS(kernel)] )"); // Error location is incorrect: #3063 test_error("kprobe:f { pid < 10000 ? 3 : cat(\"/proc/uptime\") }", R"( stdin:1:12-50: ERROR: Ternary operator must return the same type: have 'int64' and 'none' kprobe:f { pid < 10000 ? 3 : cat("/proc/uptime") } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); // Error location is incorrect: #3063 test_error("kprobe:f { @x = pid < 10000 ? 1 : \"high\" }", R"( stdin:1:17-42: ERROR: Ternary operator must return the same type: have 'int64' and 'string[5]' kprobe:f { @x = pid < 10000 ? 1 : "high" } ~~~~~~~~~~~~~~~~~~~~~~~~~ )"); // Error location is incorrect: #3063 test_error("kprobe:f { @x = pid < 10000 ? \"lo\" : 2 }", R"( stdin:1:17-40: ERROR: Ternary operator must return the same type: have 'string[3]' and 'int64' kprobe:f { @x = pid < 10000 ? "lo" : 2 } ~~~~~~~~~~~~~~~~~~~~~~~ )"); // Error location is incorrect: #3063 test_error("kprobe:f { @x = pid < 10000 ? (1, 2) : (\"a\", 4) }", R"( stdin:1:17-49: ERROR: Ternary operator must return the same type: have '(int64,int64)' and '(string[2],int64)' kprobe:f { @x = pid < 10000 ? (1, 2) : ("a", 4) } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); // Error location is incorrect: #3063 test_error("kprobe:f { @x = pid < 10000 ? ustack(1) : ustack(2) }", R"( stdin:1:17-53: ERROR: Ternary operator must have the same stack type on the right and left sides. kprobe:f { @x = pid < 10000 ? ustack(1) : ustack(2) } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); // Error location is incorrect: #3063 test_error("kprobe:f { @x = pid < 10000 ? kstack(raw) : kstack(perf) }", R"( stdin:1:17-58: ERROR: Ternary operator must have the same stack type on the right and left sides. kprobe:f { @x = pid < 10000 ? kstack(raw) : kstack(perf) } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(semantic_analyser, mismatched_call_types) { test_error("kprobe:f { @x = 1; @x = count(); }", R"( stdin:1:20-22: ERROR: Type mismatch for @x: trying to assign value of type 'count_t' when map already contains a value of type 'int64' kprobe:f { @x = 1; @x = count(); } ~~ )"); test_error("kprobe:f { @x = count(); @x = sum(pid); }", R"( stdin:1:26-28: ERROR: Type mismatch for @x: trying to assign value of type 'usum_t' when map already contains a value of type 'count_t' kprobe:f { @x = count(); @x = sum(pid); } ~~ )"); test_error("kprobe:f { @x = 1; @x = hist(0); }", R"( stdin:1:20-22: ERROR: Type mismatch for @x: trying to assign value of type 'hist_t' when map already contains a value of type 'int64' kprobe:f { @x = 1; @x = hist(0); } ~~ )"); } TEST(semantic_analyser, compound_left) { test_error("kprobe:f { $a <<= 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a <<= 0 } ~~ )"); test("kprobe:f { $a = 0; $a <<= 1 }"); test("kprobe:f { @a <<= 1 }"); } TEST(semantic_analyser, compound_right) { test_error("kprobe:f { $a >>= 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a >>= 0 } ~~ )"); test("kprobe:f { $a = 0; $a >>= 1 }"); test("kprobe:f { @a >>= 1 }"); } TEST(semantic_analyser, compound_plus) { test_error("kprobe:f { $a += 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a += 0 } ~~ )"); test("kprobe:f { $a = 0; $a += 1 }"); test("kprobe:f { @a += 1 }"); } TEST(semantic_analyser, compound_minus) { test_error("kprobe:f { $a -= 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a -= 0 } ~~ )"); test("kprobe:f { $a = 0; $a -= 1 }"); test("kprobe:f { @a -= 1 }"); } TEST(semantic_analyser, compound_mul) { test_error("kprobe:f { $a *= 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a *= 0 } ~~ )"); test("kprobe:f { $a = 0; $a *= 1 }"); test("kprobe:f { @a *= 1 }"); } TEST(semantic_analyser, compound_div) { test_error("kprobe:f { $a /= 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a /= 0 } ~~ )"); test("kprobe:f { $a = 0; $a /= 1 }"); test("kprobe:f { @a /= 1 }"); } TEST(semantic_analyser, compound_mod) { test_error("kprobe:f { $a %= 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a %= 0 } ~~ )"); test("kprobe:f { $a = 0; $a %= 1 }"); test("kprobe:f { @a %= 1 }"); } TEST(semantic_analyser, compound_band) { test_error("kprobe:f { $a &= 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a &= 0 } ~~ )"); test("kprobe:f { $a = 0; $a &= 1 }"); test("kprobe:f { @a &= 1 }"); } TEST(semantic_analyser, compound_bor) { test_error("kprobe:f { $a |= 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a |= 0 } ~~ )"); test("kprobe:f { $a = 0; $a |= 1 }"); test("kprobe:f { @a |= 1 }"); } TEST(semantic_analyser, compound_bxor) { test_error("kprobe:f { $a ^= 0 }", R"( stdin:1:12-14: ERROR: Undefined or undeclared variable: $a kprobe:f { $a ^= 0 } ~~ )"); test("kprobe:f { $a = 0; $a ^= 1 }"); test("kprobe:f { @a ^= 1 }"); } TEST(semantic_analyser, call_hist) { test("kprobe:f { @x = hist(1); }"); test("kprobe:f { @x = hist(1, 0); }"); test("kprobe:f { @x = hist(1, 5); }"); test_error("kprobe:f { @x = hist(1, 10); }", R"( stdin:1:17-28: ERROR: hist: bits 10 must be 0..5 kprobe:f { @x = hist(1, 10); } ~~~~~~~~~~~ )"); test_error("kprobe:f { $n = 3; @x = hist(1, $n); }", R"( stdin:1:25-36: ERROR: hist() expects a int literal (int provided) kprobe:f { $n = 3; @x = hist(1, $n); } ~~~~~~~~~~~ )"); test_error("kprobe:f { @x = hist(); }", R"( stdin:1:17-23: ERROR: hist() requires at least one argument (0 provided) kprobe:f { @x = hist(); } ~~~~~~ )"); test_error("kprobe:f { hist(1); }", R"( stdin:1:12-19: ERROR: hist() should be directly assigned to a map kprobe:f { hist(1); } ~~~~~~~ )"); test_error("kprobe:f { $x = hist(1); }", R"( stdin:1:17-24: ERROR: hist() should be directly assigned to a map kprobe:f { $x = hist(1); } ~~~~~~~ )"); test_error("kprobe:f { @x[hist(1)] = 1; }", R"( stdin:1:12-22: ERROR: hist() should be directly assigned to a map kprobe:f { @x[hist(1)] = 1; } ~~~~~~~~~~ stdin:1:12-22: ERROR: hist_t cannot be used as a map key kprobe:f { @x[hist(1)] = 1; } ~~~~~~~~~~ )"); test_error("kprobe:f { if(hist()) { 123 } }", R"( stdin:1:12-21: ERROR: hist() should be directly assigned to a map kprobe:f { if(hist()) { 123 } } ~~~~~~~~~ stdin:1:12-21: ERROR: hist() requires at least one argument (0 provided) kprobe:f { if(hist()) { 123 } } ~~~~~~~~~ )"); test_error("kprobe:f { hist() ? 0 : 1; }", R"( stdin:1:12-18: ERROR: hist() should be directly assigned to a map kprobe:f { hist() ? 0 : 1; } ~~~~~~ stdin:1:12-18: ERROR: hist() requires at least one argument (0 provided) kprobe:f { hist() ? 0 : 1; } ~~~~~~ )"); } TEST(semantic_analyser, call_lhist) { test("kprobe:f { @ = lhist(5, 0, 10, 1); }"); test_error("kprobe:f { @ = lhist(5, 0, 10); }", R"( stdin:1:16-31: ERROR: lhist() requires 4 arguments (3 provided) kprobe:f { @ = lhist(5, 0, 10); } ~~~~~~~~~~~~~~~ )"); test_error("kprobe:f { @ = lhist(5, 0); }", R"( stdin:1:16-27: ERROR: lhist() requires 4 arguments (2 provided) kprobe:f { @ = lhist(5, 0); } ~~~~~~~~~~~ )"); test_error("kprobe:f { @ = lhist(5); }", R"( stdin:1:16-24: ERROR: lhist() requires 4 arguments (1 provided) kprobe:f { @ = lhist(5); } ~~~~~~~~ )"); test_error("kprobe:f { @ = lhist(); }", R"( stdin:1:16-23: ERROR: lhist() requires 4 arguments (0 provided) kprobe:f { @ = lhist(); } ~~~~~~~ )"); test_error("kprobe:f { @ = lhist(5, 0, 10, 1, 2); }", R"( stdin:1:16-37: ERROR: lhist() requires 4 arguments (5 provided) kprobe:f { @ = lhist(5, 0, 10, 1, 2); } ~~~~~~~~~~~~~~~~~~~~~ )"); test_error("kprobe:f { lhist(-10, -10, 10, 1); }", R"( stdin:1:12-34: ERROR: lhist() should be directly assigned to a map kprobe:f { lhist(-10, -10, 10, 1); } ~~~~~~~~~~~~~~~~~~~~~~ )"); test_error("kprobe:f { @ = lhist(-10, -10, 10, 1); }", R"( stdin:1:16-38: ERROR: lhist() min must be non-negative (provided min -10) kprobe:f { @ = lhist(-10, -10, 10, 1); } ~~~~~~~~~~~~~~~~~~~~~~ )"); test_error("kprobe:f { $x = lhist(); }", R"( stdin:1:17-24: ERROR: lhist() should be directly assigned to a map kprobe:f { $x = lhist(); } ~~~~~~~ stdin:1:17-24: ERROR: lhist() requires 4 arguments (0 provided) kprobe:f { $x = lhist(); } ~~~~~~~ )"); test_error("kprobe:f { @[lhist()] = 1; }", R"( stdin:1:12-21: ERROR: lhist() should be directly assigned to a map kprobe:f { @[lhist()] = 1; } ~~~~~~~~~ stdin:1:12-21: ERROR: lhist() requires 4 arguments (0 provided) kprobe:f { @[lhist()] = 1; } ~~~~~~~~~ stdin:1:12-21: ERROR: lhist_t cannot be used as a map key kprobe:f { @[lhist()] = 1; } ~~~~~~~~~ )"); test_error("kprobe:f { if(lhist()) { 123 } }", R"( stdin:1:12-22: ERROR: lhist() should be directly assigned to a map kprobe:f { if(lhist()) { 123 } } ~~~~~~~~~~ stdin:1:12-22: ERROR: lhist() requires 4 arguments (0 provided) kprobe:f { if(lhist()) { 123 } } ~~~~~~~~~~ )"); test_error("kprobe:f { lhist() ? 0 : 1; }", R"( stdin:1:12-19: ERROR: lhist() should be directly assigned to a map kprobe:f { lhist() ? 0 : 1; } ~~~~~~~ stdin:1:12-19: ERROR: lhist() requires 4 arguments (0 provided) kprobe:f { lhist() ? 0 : 1; } ~~~~~~~ )"); } TEST(semantic_analyser, call_lhist_posparam) { BPFtrace bpftrace; bpftrace.add_param("0"); bpftrace.add_param("10"); bpftrace.add_param("1"); bpftrace.add_param("hello"); test(bpftrace, "kprobe:f { @ = lhist(5, $1, $2, $3); }"); test(bpftrace, "kprobe:f { @ = lhist(5, $1, $2, $4); }", 3); } TEST(semantic_analyser, call_count) { test("kprobe:f { @x = count(); }"); test("kprobe:f { @x = count(1); }", 1); test("kprobe:f { count(); }", 1); test("kprobe:f { $x = count(); }", 1); test("kprobe:f { @[count()] = 1; }", 1); test("kprobe:f { if(count()) { 123 } }", 1); test("kprobe:f { count() ? 0 : 1; }", 1); } TEST(semantic_analyser, call_sum) { test("kprobe:f { @x = sum(123); }"); test("kprobe:f { @x = sum(); }", 1); test("kprobe:f { @x = sum(123, 456); }", 1); test("kprobe:f { sum(123); }", 1); test("kprobe:f { $x = sum(123); }", 1); test("kprobe:f { @[sum(123)] = 1; }", 1); test("kprobe:f { if(sum(1)) { 123 } }", 1); test("kprobe:f { sum(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_min) { test("kprobe:f { @x = min(123); }"); test("kprobe:f { @x = min(); }", 1); test("kprobe:f { min(123); }", 1); test("kprobe:f { $x = min(123); }", 1); test("kprobe:f { @[min(123)] = 1; }", 1); test("kprobe:f { if(min(1)) { 123 } }", 1); test("kprobe:f { min(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_max) { test("kprobe:f { @x = max(123); }"); test("kprobe:f { @x = max(); }", 1); test("kprobe:f { max(123); }", 1); test("kprobe:f { $x = max(123); }", 1); test("kprobe:f { @[max(123)] = 1; }", 1); test("kprobe:f { if(max(1)) { 123 } }", 1); test("kprobe:f { max(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_avg) { test("kprobe:f { @x = avg(123); }"); test("kprobe:f { @x = avg(); }", 1); test("kprobe:f { avg(123); }", 1); test("kprobe:f { $x = avg(123); }", 1); test("kprobe:f { @[avg(123)] = 1; }", 1); test("kprobe:f { if(avg(1)) { 123 } }", 1); test("kprobe:f { avg(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_stats) { test("kprobe:f { @x = stats(123); }"); test("kprobe:f { @x = stats(); }", 1); test("kprobe:f { stats(123); }", 1); test("kprobe:f { $x = stats(123); }", 1); test("kprobe:f { @[stats(123)] = 1; }", 1); test("kprobe:f { if(stats(1)) { 123 } }", 1); test("kprobe:f { stats(1) ? 0 : 1; }", 1); } TEST(semantic_analyser, call_delete) { test("kprobe:f { @x = 1; delete(@x); }"); test("kprobe:f { @y[5] = 5; delete(@y, 5); }"); test("kprobe:f { @a[1] = 1; delete(@a, @a[1]); }"); test("kprobe:f { @a = 1; @b[2] = 2; delete(@b, @a); }"); test("kprobe:f { @a[1] = 1; $x = 1; delete(@a, $x); }"); test(R"(kprobe:f { @y["hi"] = 5; delete(@y, "longerstr"); })"); test(R"(kprobe:f { @y["hi", 5] = 5; delete(@y, ("hi", 5)); })"); test(R"(kprobe:f { @y["longerstr", 5] = 5; delete(@y, ("hi", 5)); })"); test(R"(kprobe:f { @y["hi", 5] = 5; delete(@y, ("longerstr", 5)); })"); test("kprobe:f { @y[(3, 4, 5)] = 5; delete(@y, (1, 2, 3)); }"); test("kprobe:f { @y[((int8)3, 4, 5)] = 5; delete(@y, (1, 2, 3)); }"); test("kprobe:f { @y[(3, 4, 5)] = 5; delete(@y, ((int8)1, 2, 3)); }"); // The second arg gets treated like a map key, in terms of int type adjustment test("kprobe:f { @y[5] = 5; delete(@y, (uint8)5); }"); test("kprobe:f { @y[5, 4] = 5; delete(@y, ((uint8)5, (uint64)4)); }"); test_error("kprobe:f { delete(1); }", R"( stdin:1:12-20: ERROR: delete() expects a map for the first argument and a key for the second argument e.g. `delete(@my_map, 1);` kprobe:f { delete(1); } ~~~~~~~~ )"); test_error("kprobe:f { @y = delete(@x); }", R"( stdin:1:17-27: ERROR: delete() should not be used in an assignment or as a map key kprobe:f { @y = delete(@x); } ~~~~~~~~~~ )"); test_error("kprobe:f { $y = delete(@x); }", R"( stdin:1:17-27: ERROR: delete() should not be used in an assignment or as a map key kprobe:f { $y = delete(@x); } ~~~~~~~~~~ )"); test_error("kprobe:f { @[delete(@x)] = 1; }", R"( stdin:1:12-24: ERROR: delete() should not be used in an assignment or as a map key kprobe:f { @[delete(@x)] = 1; } ~~~~~~~~~~~~ )"); test_error("kprobe:f { @x = 1; if(delete(@x)) { 123 } }", R"( stdin:1:20-42: ERROR: Invalid condition in if(): none kprobe:f { @x = 1; if(delete(@x)) { 123 } } ~~~~~~~~~~~~~~~~~~~~~~ )"); test_error("kprobe:f { @x = 1; delete(@x) ? 0 : 1; }", R"( stdin:1:20-39: ERROR: Invalid condition in ternary: none kprobe:f { @x = 1; delete(@x) ? 0 : 1; } ~~~~~~~~~~~~~~~~~~~ )"); test_error("kprobe:f { @y[5] = 5; delete(@y[5], 5); }", R"( stdin:1:23-35: ERROR: delete() expects a map with no keys for the first argument kprobe:f { @y[5] = 5; delete(@y[5], 5); } ~~~~~~~~~~~~ )"); test_error("kprobe:f { @y[(3, 4, 5)] = 5; delete(@y, (1, 2)); }", R"( stdin:1:42-48: ERROR: Argument mismatch for @y: trying to access with arguments: '(int64,int64)' when map expects arguments: '(int64,int64,int64)' kprobe:f { @y[(3, 4, 5)] = 5; delete(@y, (1, 2)); } ~~~~~~ )"); test_error("kprobe:f { @y[1] = 2; delete(@y); }", R"( stdin:1:23-32: ERROR: delete() expects a map for the first argument and a key for the second argument e.g. `delete(@my_map, 1);` kprobe:f { @y[1] = 2; delete(@y); } ~~~~~~~~~ )"); test_error("kprobe:f { @a[1] = 1; delete(@a, @a); }", R"( stdin:1:34-36: ERROR: Argument mismatch for @a: trying to access with no arguments when map expects arguments: 'int64' kprobe:f { @a[1] = 1; delete(@a, @a); } ~~ )"); // Deprecated API test("kprobe:f { @x = 1; delete(@x); }"); test("kprobe:f { @y[5] = 5; delete(@y[5]); }"); test(R"(kprobe:f { @y[1, "hi"] = 5; delete(@y[1, "longerstr"]); })"); test(R"(kprobe:f { @y[1, "longerstr"] = 5; delete(@y[1, "hi"]); })"); test_error("kprobe:f { @x = 1; @y = 5; delete(@x, @y); }", R"( stdin:1:39-41: ERROR: Argument mismatch for @x: trying to access with arguments: 'int64' when map expects no arguments kprobe:f { @x = 1; @y = 5; delete(@x, @y); } ~~ )"); test_error(R"(kprobe:f { @x[1, "hi"] = 1; delete(@x["hi", 1]); })", R"( stdin:1:29-47: ERROR: Argument mismatch for @x: trying to access with arguments: '(string[3],int64)' when map expects arguments: '(int64,string[3])' kprobe:f { @x[1, "hi"] = 1; delete(@x["hi", 1]); } ~~~~~~~~~~~~~~~~~~ )"); test_error("kprobe:f { @x = 1; @y[5] = 5; delete(@x, @y[5], @y[6]); }", R"( stdin:1:31-55: ERROR: delete() takes up to 2 arguments (3 provided) kprobe:f { @x = 1; @y[5] = 5; delete(@x, @y[5], @y[6]); } ~~~~~~~~~~~~~~~~~~~~~~~~ )"); test_error("kprobe:f { @x = 1; delete(@x[1]); }", R"( stdin:1:20-31: ERROR: Argument mismatch for @x: trying to access with arguments: 'int64' when map expects no arguments kprobe:f { @x = 1; delete(@x[1]); } ~~~~~~~~~~~ )"); } TEST(semantic_analyser, call_exit) { test("kprobe:f { exit(); }"); test("kprobe:f { exit(1); }"); test("kprobe:f { $a = 1; exit($a); }"); test("kprobe:f { @a = exit(); }", 1); test("kprobe:f { @a = exit(1); }", 1); test("kprobe:f { $a = exit(1); }", 1); test("kprobe:f { @[exit(1)] = 1; }", 1); test("kprobe:f { if(exit()) { 123 } }", 2); test("kprobe:f { exit() ? 0 : 1; }", 2); test_error("kprobe:f { exit(1, 2); }", R"( stdin:1:12-22: ERROR: exit() takes up to one argument (2 provided) kprobe:f { exit(1, 2); } ~~~~~~~~~~ )"); test_error("kprobe:f { $a = \"1\"; exit($a); }", R"( stdin:1:22-30: ERROR: exit() only supports int arguments (string provided) kprobe:f { $a = "1"; exit($a); } ~~~~~~~~ )"); } TEST(semantic_analyser, call_print) { test("kprobe:f { @x = count(); print(@x); }"); test("kprobe:f { @x = count(); print(@x, 5); }"); test("kprobe:f { @x = count(); print(@x, 5, 10); }"); test("kprobe:f { @x = count(); print(@x, 5, 10, 1); }", 1); test("kprobe:f { @x = count(); @x = print(); }", 1); test("kprobe:f { print(@x); @x[1,2] = count(); }"); test("kprobe:f { @x[1,2] = count(); print(@x); }"); test("kprobe:f { @x = count(); @ = print(@x); }", 1); test("kprobe:f { @x = count(); $y = print(@x); }", 1); test("kprobe:f { @x = count(); @[print(@x)] = 1; }", 1); test("kprobe:f { @x = count(); if(print(@x)) { 123 } }", 3); test("kprobe:f { @x = count(); print(@x) ? 0 : 1; }", 3); test_for_warning("kprobe:f { @x = stats(10); print(@x, 2); }", "top and div arguments are ignored"); test_for_warning("kprobe:f { @x = stats(10); print(@x, 2, 3); }", "top and div arguments are ignored"); } TEST(semantic_analyser, call_print_map_item) { test(R"_(BEGIN { @x[1] = 1; print(@x[1]); })_"); test(R"_(BEGIN { @x[1] = 1; @x[2] = 2; print(@x[2]); })_"); test(R"_(BEGIN { @x[1] = 1; print(@x[2]); })_"); test(R"_(BEGIN { @x[3, 5] = 1; print(@x[3, 5]); })_"); test(R"_(BEGIN { @x[1,2] = "asdf"; print((1, 2, @x[1,2])); })_"); test_error("BEGIN { @x[1] = 1; print(@x[\"asdf\"]); }", R"( stdin:1:20-36: ERROR: Argument mismatch for @x: trying to access with arguments: 'string[5]' when map expects arguments: 'int64' BEGIN { @x[1] = 1; print(@x["asdf"]); } ~~~~~~~~~~~~~~~~ )"); test_error("BEGIN { print(@x[2]); }", R"( stdin:1:9-20: ERROR: Undefined map: @x BEGIN { print(@x[2]); } ~~~~~~~~~~~ )"); test_error("BEGIN { @x[1] = 1; print(@x[1], 3, 5); }", R"( stdin:1:20-38: ERROR: Single-value (i.e. indexed) map print cannot take additional arguments. BEGIN { @x[1] = 1; print(@x[1], 3, 5); } ~~~~~~~~~~~~~~~~~~ )"); test_error("BEGIN { @x[1] = hist(10); print(@x[1]); }", R"( stdin:1:27-39: ERROR: Map type hist_t cannot print the value of individual keys. You must print the whole map. BEGIN { @x[1] = hist(10); print(@x[1]); } ~~~~~~~~~~~~ )"); } TEST(semantic_analyser, call_print_non_map) { test(R"_(BEGIN { print(1) })_"); test(R"_(BEGIN { print(comm) })_"); test(R"_(BEGIN { print(nsecs) })_"); test(R"_(BEGIN { print("string") })_"); test(R"_(BEGIN { print((1, 2, "tuple")) })_"); test(R"_(BEGIN { $x = 1; print($x) })_"); test(R"_(BEGIN { $x = 1; $y = $x + 3; print($y) })_"); test(R"_(BEGIN { print((int8 *)0) })_"); test(R"_(BEGIN { print(3, 5) })_", 1); test(R"_(BEGIN { print(3, 5, 2) })_", 1); test(R"_(BEGIN { print(exit()) })_", 2); test(R"_(BEGIN { print(count()) })_", 1); test(R"_(BEGIN { print(ctx) })_", 1); } TEST(semantic_analyser, call_clear) { test("kprobe:f { @x = count(); clear(@x); }"); test("kprobe:f { @x = count(); clear(@x, 1); }", 1); test("kprobe:f { @x = count(); @x = clear(); }", 1); test("kprobe:f { clear(@x); @x[1,2] = count(); }"); test("kprobe:f { @x[1,2] = count(); clear(@x); }"); test("kprobe:f { @x[1,2] = count(); clear(@x[3,4]); }", 1); test("kprobe:f { @x = count(); @ = clear(@x); }", 1); test("kprobe:f { @x = count(); $y = clear(@x); }", 1); test("kprobe:f { @x = count(); @[clear(@x)] = 1; }", 1); test("kprobe:f { @x = count(); if(clear(@x)) { 123 } }", 3); test("kprobe:f { @x = count(); clear(@x) ? 0 : 1; }", 3); } TEST(semantic_analyser, call_zero) { test("kprobe:f { @x = count(); zero(@x); }"); test("kprobe:f { @x = count(); zero(@x, 1); }", 1); test("kprobe:f { @x = count(); @x = zero(); }", 1); test("kprobe:f { zero(@x); @x[1,2] = count(); }"); test("kprobe:f { @x[1,2] = count(); zero(@x); }"); test("kprobe:f { @x[1,2] = count(); zero(@x[3,4]); }", 1); test("kprobe:f { @x = count(); @ = zero(@x); }", 1); test("kprobe:f { @x = count(); $y = zero(@x); }", 1); test("kprobe:f { @x = count(); @[zero(@x)] = 1; }", 1); test("kprobe:f { @x = count(); if(zero(@x)) { 123 } }", 3); test("kprobe:f { @x = count(); zero(@x) ? 0 : 1; }", 3); } TEST(semantic_analyser, call_len) { test("kprobe:f { @x[0] = 0; len(@x); }"); test("kprobe:f { @x[0] = 0; len(); }", 1); test("kprobe:f { @x[0] = 0; len(@x, 1); }", 1); test("kprobe:f { @x[0] = 0; len(@x[2]); }", 1); test("kprobe:f { $x = 0; len($x); }", 1); test("kprobe:f { len(ustack) }"); test("kprobe:f { len(kstack) }"); test_error("kprobe:f { len(0) }", R"( stdin:1:12-18: ERROR: len() expects a map or stack to be provided kprobe:f { len(0) } ~~~~~~ )"); } TEST(semantic_analyser, call_has_key) { test("kprobe:f { @x[1] = 0; if (has_key(@x, 1)) {} }"); test("kprobe:f { @x[1, 2] = 0; if (has_key(@x, (3, 4))) {} }"); test("kprobe:f { @x[1, (int8)2] = 0; if (has_key(@x, (3, 4))) {} }"); test(R"(kprobe:f { @x[1, "hi"] = 0; if (has_key(@x, (2, "bye"))) {} })"); test( R"(kprobe:f { @x[1, "hi"] = 0; if (has_key(@x, (2, "longerstr"))) {} })"); test( R"(kprobe:f { @x[1, "longerstr"] = 0; if (has_key(@x, (2, "hi"))) {} })"); test("kprobe:f { @x[1, 2] = 0; $a = (3, 4); if (has_key(@x, $a)) {} }"); test("kprobe:f { @x[1, 2] = 0; @a = (3, 4); if (has_key(@x, @a)) {} }"); test("kprobe:f { @x[1, 2] = 0; @a[1] = (3, 4); if (has_key(@x, @a[1])) {} }"); test("kprobe:f { @x[1] = 0; @a = has_key(@x, 1); }"); test("kprobe:f { @x[1] = 0; $a = has_key(@x, 1); }"); test("kprobe:f { @x[1] = 0; @a[has_key(@x, 1)] = 1; }"); test_error("kprobe:f { @x[1] = 1; if (has_key(@x)) {} }", R"( stdin:1:27-39: ERROR: has_key() requires at least 2 arguments (1 provided) kprobe:f { @x[1] = 1; if (has_key(@x)) {} } ~~~~~~~~~~~~ )"); test_error("kprobe:f { @x[1] = 1; if (has_key(@x[1], 1)) {} }", R"( stdin:1:27-41: ERROR: has_key() expects the first argument to be a map. Not a map value expression. kprobe:f { @x[1] = 1; if (has_key(@x[1], 1)) {} } ~~~~~~~~~~~~~~ )"); test_error("kprobe:f { @x = 1; if (has_key(@x, 1)) {} }", R"( stdin:1:24-35: ERROR: has_key() only accepts maps that have keys. No scalar maps e.g. `@a = 1;` kprobe:f { @x = 1; if (has_key(@x, 1)) {} } ~~~~~~~~~~~ )"); test_error("kprobe:f { @x[1, 2] = 1; if (has_key(@x, 1)) {} }", R"( stdin:1:43-44: ERROR: Argument mismatch for @x: trying to access with arguments: 'int64' when map expects arguments: '(int64,int64)' kprobe:f { @x[1, 2] = 1; if (has_key(@x, 1)) {} } ~ )"); test_error(R"(kprobe:f { @x[1, "hi"] = 0; if (has_key(@x, (2, 1))) {} })", R"( stdin:1:45-51: ERROR: Argument mismatch for @x: trying to access with arguments: '(int64,int64)' when map expects arguments: '(int64,string[3])' kprobe:f { @x[1, "hi"] = 0; if (has_key(@x, (2, 1))) {} } ~~~~~~ )"); test_error("kprobe:f { @x[1] = 1; $a = 1; if (has_key($a, 1)) {} }", R"( stdin:1:34-45: ERROR: has_key() expects the first argument to be a map kprobe:f { @x[1] = 1; $a = 1; if (has_key($a, 1)) {} } ~~~~~~~~~~~ )"); test_error("kprobe:f { @a[1] = 1; has_key(@a, @a); }", R"( stdin:1:35-37: ERROR: Argument mismatch for @a: trying to access with no arguments when map expects arguments: 'int64' kprobe:f { @a[1] = 1; has_key(@a, @a); } ~~ )"); } TEST(semantic_analyser, call_time) { test("kprobe:f { time(); }"); test("kprobe:f { time(\"%M:%S\"); }"); test("kprobe:f { time(\"%M:%S\", 1); }", 1); test("kprobe:f { @x = time(); }", 1); test("kprobe:f { $x = time(); }", 1); test("kprobe:f { @[time()] = 1; }", 1); test("kprobe:f { time(1); }", 2); test("kprobe:f { $x = \"str\"; time($x); }", 2); test("kprobe:f { if(time()) { 123 } }", 2); test("kprobe:f { time() ? 0 : 1; }", 2); } TEST(semantic_analyser, call_strftime) { test("kprobe:f { strftime(\"%M:%S\", 1); }"); test("kprobe:f { strftime(\"%M:%S\", nsecs); }"); test(R"(kprobe:f { strftime("%M:%S", ""); })", 2); test("kprobe:f { strftime(1, nsecs); }", 2); test("kprobe:f { $var = \"str\"; strftime($var, nsecs); }", 2); test("kprobe:f { strftime(); }", 1); test("kprobe:f { strftime(\"%M:%S\"); }", 1); test("kprobe:f { strftime(\"%M:%S\", 1, 1); }", 1); test("kprobe:f { strftime(1, 1, 1); }", 1); test(R"(kprobe:f { strftime("%M:%S", "", 1); })", 1); test("kprobe:f { $ts = strftime(\"%M:%S\", 1); }"); test("kprobe:f { @ts = strftime(\"%M:%S\", nsecs); }"); test("kprobe:f { @[strftime(\"%M:%S\", nsecs)] = 1; }"); test(R"(kprobe:f { printf("%s", strftime("%M:%S", nsecs)); })"); test(R"(kprobe:f { strncmp("str", strftime("%M:%S", nsecs), 10); })", 2); test("kprobe:f { strftime(\"%M:%S\", nsecs(monotonic)); }", 2); test("kprobe:f { strftime(\"%M:%S\", nsecs(boot)); }"); test("kprobe:f { strftime(\"%M:%S\", nsecs(tai)); }"); } TEST(semantic_analyser, call_str) { test("kprobe:f { str(arg0); }"); test("kprobe:f { @x = str(arg0); }"); test("kprobe:f { str(); }", 1); test("kprobe:f { str(\"hello\"); }", 1); } TEST(semantic_analyser, call_str_2_lit) { test("kprobe:f { str(arg0, 3); }"); test("kprobe:f { str(arg0, -3); }", 2); test("kprobe:f { @x = str(arg0, 3); }"); test("kprobe:f { str(arg0, \"hello\"); }", 2); // Check the string size BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $x = str(arg0, 3); }"); auto x = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(0)); EXPECT_EQ(CreateString(3), x->var->type); } TEST(semantic_analyser, call_str_2_expr) { test("kprobe:f { str(arg0, arg1); }"); test("kprobe:f { @x = str(arg0, arg1); }"); } TEST(semantic_analyser, call_str_state_leak_regression_test) { // Previously, the semantic analyser would leak state in the first str() // call. This would make the semantic analyser think it's still processing // a positional parameter in the second str() call causing confusing error // messages. test(R"PROG(kprobe:f { $x = str($1) == "asdf"; $y = str(arg0, 1) })PROG"); } TEST(semantic_analyser, call_buf) { test("kprobe:f { buf(arg0, 1); }"); test("kprobe:f { buf(arg0, -1); }", 1); test("kprobe:f { @x = buf(arg0, 1); }"); test("kprobe:f { $x = buf(arg0, 1); }"); test("kprobe:f { buf(); }", 1); test("kprobe:f { buf(\"hello\"); }", 2); test("struct x { int c[4] }; kprobe:f { $foo = (struct x*)0; @x = " "buf($foo->c); }"); } TEST(semantic_analyser, call_buf_lit) { test("kprobe:f { @x = buf(arg0, 3); }"); test("kprobe:f { buf(arg0, \"hello\"); }", 2); } TEST(semantic_analyser, call_buf_expr) { test("kprobe:f { buf(arg0, arg1); }"); test("kprobe:f { @x = buf(arg0, arg1); }"); } TEST(semantic_analyser, call_buf_posparam) { BPFtrace bpftrace; bpftrace.add_param("1"); bpftrace.add_param("hello"); test(bpftrace, "kprobe:f { buf(arg0, $1); }"); test(bpftrace, "kprobe:f { buf(arg0, $2); }", 1); } TEST(semantic_analyser, call_ksym) { test("kprobe:f { ksym(arg0); }"); test("kprobe:f { @x = ksym(arg0); }"); test("kprobe:f { ksym(); }", 1); test("kprobe:f { ksym(\"hello\"); }", 1); } TEST(semantic_analyser, call_usym) { test("kprobe:f { usym(arg0); }"); test("kprobe:f { @x = usym(arg0); }"); test("kprobe:f { usym(); }", 1); test("kprobe:f { usym(\"hello\"); }", 1); } TEST(semantic_analyser, call_ntop) { std::string structs = "struct inet { unsigned char ipv4[4]; unsigned char " "ipv6[16]; unsigned char invalid[10]; } "; test("kprobe:f { ntop(2, arg0); }"); test("kprobe:f { ntop(arg0); }"); test(structs + "kprobe:f { ntop(10, ((struct inet*)0)->ipv4); }"); test(structs + "kprobe:f { ntop(10, ((struct inet*)0)->ipv6); }"); test(structs + "kprobe:f { ntop(((struct inet*)0)->ipv4); }"); test(structs + "kprobe:f { ntop(((struct inet*)0)->ipv6); }"); test("kprobe:f { @x = ntop(2, arg0); }"); test("kprobe:f { @x = ntop(arg0); }"); test("kprobe:f { @x = ntop(2, 0xFFFF); }"); test("kprobe:f { @x = ntop(0xFFFF); }"); test(structs + "kprobe:f { @x = ntop(((struct inet*)0)->ipv4); }"); test(structs + "kprobe:f { @x = ntop(((struct inet*)0)->ipv6); }"); // Regression test that ntop can use arguments from the prog context test("tracepoint:tcp:some_tcp_tp { ntop(args.saddr_v6); }"); test("kprobe:f { ntop(); }", 1); test("kprobe:f { ntop(2, \"hello\"); }", 1); test("kprobe:f { ntop(\"hello\"); }", 1); test(structs + "kprobe:f { ntop(((struct inet*)0)->invalid); }", 1); } TEST(semantic_analyser, call_pton) { test("kprobe:f { $addr_v4 = pton(\"127.0.0.1\"); }"); test("kprobe:f { $addr_v4 = pton(\"127.0.0.1\"); $b1 = $addr_v4[0]; }"); test("kprobe:f { $addr_v6 = pton(\"::1\"); }"); test("kprobe:f { $addr_v6 = pton(\"::1\"); $b1 = $addr_v6[0]; }"); std::string def = "#define AF_INET 2\n #define AF_INET6 10\n"; test("kprobe:f { $addr_v4_text = ntop(pton(\"127.0.0.1\")); }"); test(def + "kprobe:f { $addr_v4_text = ntop(AF_INET, pton(\"127.0.0.1\")); }"); test(def + "kprobe:f { $addr_v6_text = ntop(AF_INET6, pton(\"::1\")); }"); test("kprobe:f { $addr_v4 = pton(); }", 1); test("kprobe:f { $addr_v4 = pton(\"\"); }", 1); test("kprobe:f { $addr_v4 = pton(\"127.0.1\"); }", 1); test("kprobe:f { $addr_v4 = pton(\"127.0.0.0.1\"); }", 1); test("kprobe:f { $addr_v6 = pton(\":\"); }", 1); test("kprobe:f { $addr_v6 = pton(\"1:1:1:1:1:1:1:1:1\"); }", 1); std::string structs = "struct inet { unsigned char non_literal_string[4]; } "; test("kprobe:f { $addr_v4 = pton(1); }", 1); test(structs + "kprobe:f { $addr_v4 = pton(((struct " "inet*)0)->non_literal_string); }", 1); } TEST(semantic_analyser, call_kaddr) { test("kprobe:f { kaddr(\"avenrun\"); }"); test("kprobe:f { @x = kaddr(\"avenrun\"); }"); test("kprobe:f { kaddr(); }", 1); test("kprobe:f { kaddr(123); }", 1); } TEST(semantic_analyser, call_uaddr) { test("u:/bin/sh:main { uaddr(\"github.com/golang/glog.severityName\"); }"); test("uprobe:/bin/sh:main { uaddr(\"glob_asciirange\"); }"); test("u:/bin/sh:main,u:/bin/sh:readline { uaddr(\"glob_asciirange\"); }"); test("uprobe:/bin/sh:main { @x = uaddr(\"glob_asciirange\"); }"); test("uprobe:/bin/sh:main { uaddr(); }", 1); test("uprobe:/bin/sh:main { uaddr(123); }", 1); test("uprobe:/bin/sh:main { uaddr(\"?\"); }", 1); test("uprobe:/bin/sh:main { $str = \"glob_asciirange\"; uaddr($str); }", 1); test("uprobe:/bin/sh:main { @str = \"glob_asciirange\"; uaddr(@str); }", 1); test("k:f { uaddr(\"A\"); }", 1); test("i:s:1 { uaddr(\"A\"); }", 1); // The C struct parser should set the is_signed flag on signed types BPFtrace bpftrace; Driver driver(bpftrace); std::string prog = "uprobe:/bin/sh:main {" "$a = uaddr(\"12345_1\");" "$b = uaddr(\"12345_2\");" "$c = uaddr(\"12345_4\");" "$d = uaddr(\"12345_8\");" "$e = uaddr(\"12345_5\");" "$f = uaddr(\"12345_33\");" "}"; test(driver, prog); std::vector sizes = { 8, 16, 32, 64, 64, 64 }; for (size_t i = 0; i < sizes.size(); i++) { auto v = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(i)); EXPECT_TRUE(v->var->type.IsPtrTy()); EXPECT_TRUE(v->var->type.GetPointeeTy()->IsIntTy()); EXPECT_EQ((unsigned long int)sizes.at(i), v->var->type.GetPointeeTy()->GetIntBitWidth()); } } TEST(semantic_analyser, call_cgroupid) { // Handle args above default max-string length (64) test("kprobe:f { cgroupid(" // 1 2 3 4 5 6 "\"123456789/123456789/123456789/123456789/123456789/123456789/12345\"" "); }"); } TEST(semantic_analyser, call_reg) { #ifdef __x86_64__ test("kprobe:f { reg(\"ip\"); }"); test("kprobe:f { @x = reg(\"ip\"); }"); #endif test("kprobe:f { reg(\"blah\"); }", 1); test("kprobe:f { reg(); }", 1); test("kprobe:f { reg(123); }", 1); } TEST(semantic_analyser, call_func) { test("kprobe:f { @[func] = count(); }"); test("kprobe:f { printf(\"%s\", func); }"); test("uprobe:/bin/sh:f { @[func] = count(); }"); test("uprobe:/bin/sh:f { printf(\"%s\", func); }"); test("fentry:f { func }"); test("fexit:f { func }"); test("kretprobe:f { func }"); test("uretprobe:/bin/sh:f { func }"); // We only care about the BPF_FUNC_get_func_ip feature and error message here, // but don't have enough control over the mock features to only disable that. test_error("fentry:f { func }", R"( stdin:1:1-9: ERROR: fentry/fexit not available for your kernel version. fentry:f { func } ~~~~~~~~ stdin:1:12-16: ERROR: BPF_FUNC_get_func_ip not available for your kernel version fentry:f { func } ~~~~ )", false); test_error("fexit:f { func }", R"( stdin:1:1-8: ERROR: fentry/fexit not available for your kernel version. fexit:f { func } ~~~~~~~ stdin:1:11-15: ERROR: BPF_FUNC_get_func_ip not available for your kernel version fexit:f { func } ~~~~ )", false); test_error("kretprobe:f { func }", R"( stdin:1:15-19: ERROR: The 'func' builtin is not available for kretprobes on kernels without the get_func_ip BPF feature. Consider using the 'probe' builtin instead. kretprobe:f { func } ~~~~ )", false); test_error("uretprobe:/bin/sh:f { func }", R"( stdin:1:23-27: ERROR: The 'func' builtin is not available for uretprobes on kernels without the get_func_ip BPF feature. Consider using the 'probe' builtin instead. uretprobe:/bin/sh:f { func } ~~~~ )", false); } TEST(semantic_analyser, call_probe) { test("kprobe:f { @[probe] = count(); }"); test("kprobe:f { printf(\"%s\", probe); }"); } TEST(semantic_analyser, call_cat) { test("kprobe:f { cat(\"/proc/loadavg\"); }"); test("kprobe:f { cat(\"/proc/%d/cmdline\", 1); }"); test("kprobe:f { cat(); }", 1); test("kprobe:f { cat(123); }", 1); test("kprobe:f { @x = cat(\"/proc/loadavg\"); }", 1); test("kprobe:f { $x = cat(\"/proc/loadavg\"); }", 1); test("kprobe:f { @[cat(\"/proc/loadavg\")] = 1; }", 1); test("kprobe:f { if(cat(\"/proc/loadavg\")) { 123 } }", 2); test("kprobe:f { cat(\"/proc/loadavg\") ? 0 : 1; }", 2); } TEST(semantic_analyser, call_stack) { test("kprobe:f { kstack() }"); test("kprobe:f { ustack() }"); test("kprobe:f { kstack(bpftrace) }"); test("kprobe:f { ustack(bpftrace) }"); test("kprobe:f { kstack(perf) }"); test("kprobe:f { ustack(perf) }"); test("kprobe:f { kstack(3) }"); test("kprobe:f { ustack(3) }"); test("kprobe:f { kstack(perf, 3) }"); test("kprobe:f { ustack(perf, 3) }"); test("kprobe:f { kstack(raw, 3) }"); test("kprobe:f { ustack(raw, 3) }"); // Wrong arguments test("kprobe:f { kstack(3, perf) }", 1); test("kprobe:f { ustack(3, perf) }", 1); test("kprobe:f { kstack(perf, 3, 4) }", 1); test("kprobe:f { ustack(perf, 3, 4) }", 1); test("kprobe:f { kstack(bob) }", 1); test("kprobe:f { ustack(bob) }", 1); test("kprobe:f { kstack(\"str\") }", 1); test("kprobe:f { ustack(\"str\") }", 1); test("kprobe:f { kstack(perf, \"str\") }", 1); test("kprobe:f { ustack(perf, \"str\") }", 1); test("kprobe:f { kstack(\"str\", 3) }", 1); test("kprobe:f { ustack(\"str\", 3) }", 1); // Non-literals test("kprobe:f { @x = perf; kstack(@x) }", 1); test("kprobe:f { @x = perf; ustack(@x) }", 1); test("kprobe:f { @x = perf; kstack(@x, 3) }", 1); test("kprobe:f { @x = perf; ustack(@x, 3) }", 1); test("kprobe:f { @x = 3; kstack(@x) }", 1); test("kprobe:f { @x = 3; ustack(@x) }", 1); test("kprobe:f { @x = 3; kstack(perf, @x) }", 1); test("kprobe:f { @x = 3; ustack(perf, @x) }", 1); // Positional params BPFtrace bpftrace; bpftrace.add_param("3"); bpftrace.add_param("hello"); test(bpftrace, "kprobe:f { kstack($1) }"); test(bpftrace, "kprobe:f { ustack($1) }"); test(bpftrace, "kprobe:f { kstack(perf, $1) }"); test(bpftrace, "kprobe:f { ustack(perf, $1) }"); test(bpftrace, "kprobe:f { kstack($2) }", 1); test(bpftrace, "kprobe:f { ustack($2) }", 1); test(bpftrace, "kprobe:f { kstack(perf, $2) }", 1); test(bpftrace, "kprobe:f { ustack(perf, $2) }", 1); } TEST(semantic_analyser, call_macaddr) { std::string structs = "struct mac { char addr[6]; }; struct invalid { char addr[7]; }; "; test("kprobe:f { macaddr(arg0); }"); test(structs + "kprobe:f { macaddr((struct mac*)arg0); }"); test(structs + "kprobe:f { @x[macaddr((struct mac*)arg0)] = 1; }"); test(structs + "kprobe:f { @x = macaddr((struct mac*)arg0); }"); test(structs + "kprobe:f { printf(\"%s\", macaddr((struct mac*)arg0)); }"); test(structs + "kprobe:f { macaddr(((struct invalid*)arg0)->addr); }", 1); test(structs + "kprobe:f { macaddr(*(struct mac*)arg0); }", 1); test("kprobe:f { macaddr(); }", 1); test("kprobe:f { macaddr(\"hello\"); }", 1); } TEST(semantic_analyser, call_bswap) { test("kprobe:f { bswap(arg0); }"); test("kprobe:f { bswap(0x12); }"); test("kprobe:f { bswap(0x12 + 0x34); }"); test("kprobe:f { bswap((int8)0x12); }"); test("kprobe:f { bswap((int16)0x12); }"); test("kprobe:f { bswap((int32)0x12); }"); test("kprobe:f { bswap((int64)0x12); }"); 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) }"); test("kprobe:f { cgroup_path(1, \"hello\") }"); test("kprobe:f { cgroup_path(1, 2) }", 2); test("kprobe:f { cgroup_path(\"1\") }", 2); test("kprobe:f { printf(\"%s\", cgroup_path(1)) }"); test("kprobe:f { printf(\"%s %s\", cgroup_path(1), cgroup_path(2)) }"); test("kprobe:f { $var = cgroup_path(0); printf(\"%s %s\", $var, $var) }"); test("kprobe:f { printf(\"%d\", cgroup_path(1)) }", 2); } TEST(semantic_analyser, call_strerror) { test("kprobe:f { strerror(1) }"); test("kprobe:f { strerror(1, 2) }", 1); test("kprobe:f { strerror(\"1\") }", 2); test("kprobe:f { printf(\"%s\", strerror(1)) }"); test("kprobe:f { printf(\"%s %s\", strerror(1), strerror(2)) }"); test("kprobe:f { $var = strerror(0); printf(\"%s %s\", $var, $var) }"); test("kprobe:f { printf(\"%d\", strerror(1)) }", 2); } TEST(semantic_analyser, map_reassignment) { test("kprobe:f { @x = 1; @x = 2; }"); test("kprobe:f { @x = 1; @x = \"foo\"; }", 1); } TEST(semantic_analyser, variable_reassignment) { test("kprobe:f { $x = 1; $x = 2; }"); test("kprobe:f { $x = 1; $x = \"foo\"; }", 1); test(R"(kprobe:f { $b = "hi"; $b = @b; } kprobe:g { @b = "bye"; })"); test_error(R"(kprobe:f { $b = "hi"; $b = @b; } kprobe:g { @b = 1; })", R"( stdin:1:23-30: ERROR: Type mismatch for $b: trying to assign value of type 'int64' when variable already contains a value of type 'string[3]' kprobe:f { $b = "hi"; $b = @b; } kprobe:g { @b = 1; } ~~~~~~~ )"); } TEST(semantic_analyser, map_use_before_assign) { test("kprobe:f { @x = @y; @y = 2; }"); } TEST(semantic_analyser, variable_use_before_assign) { test("kprobe:f { @x = $y; $y = 2; }", 1); } TEST(semantic_analyser, maps_are_global) { test("kprobe:f { @x = 1 } kprobe:g { @y = @x }"); test("kprobe:f { @x = 1 } kprobe:g { @x = \"abc\" }", 1); } TEST(semantic_analyser, variables_are_local) { test("kprobe:f { $x = 1 } kprobe:g { $x = \"abc\"; }"); test("kprobe:f { $x = 1 } kprobe:g { @y = $x }", 1); } TEST(semantic_analyser, array_access) { test("kprobe:f { $s = arg0; @x = $s->y[0];}", 3); test("kprobe:f { $s = 0; @x = $s->y[0];}", 3); test("struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[5];}", 3); test("struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[-1];}", 3); test("struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[\"0\"];}", 3); test("struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; $idx = 0; @x = $s->y[$idx];}", 3); test("kprobe:f { $s = arg0; @x = $s[0]; }", 3); test("struct MyStruct { void *y; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[5];}", 3); BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "struct MyStruct { int y[4]; } kprobe:f { $s = (struct MyStruct *) " "arg0; @x = $s->y[0];}"); auto assignment = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(1)); EXPECT_EQ(CreateInt64(), assignment->map->type); test(driver, "struct MyStruct { int y[4]; } kprobe:f { $s = ((struct MyStruct *) " "arg0)->y; @x = $s[0];}"); auto array_var_assignment = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(0)); EXPECT_EQ(CreateArray(4, CreateInt32()), array_var_assignment->var->type); test(driver, "struct MyStruct { int y[4]; } kprobe:f { @a[0] = ((struct MyStruct *) " "arg0)->y; @x = @a[0][0];}"); auto array_map_assignment = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(0)); EXPECT_EQ(CreateArray(4, CreateInt32()), array_map_assignment->map->type); test(driver, "kprobe:f { $s = (int32 *) arg0; $x = $s[0]; }"); auto var_assignment = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(1)); EXPECT_EQ(CreateInt32(), var_assignment->var->type); // Positional parameter as index bpftrace.add_param("0"); bpftrace.add_param("hello"); test(bpftrace, "struct MyStruct { int y[4]; } " "kprobe:f { $s = ((struct MyStruct *)arg0)->y[$1]; }"); test(bpftrace, "struct MyStruct { int y[4]; } " "kprobe:f { $s = ((struct MyStruct *)arg0)->y[$2]; }", 2); test(bpftrace, "struct MyStruct { int x; int y[]; } " "kprobe:f { $s = (struct MyStruct *) " "arg0; @y = $s->y[0];}"); } TEST(semantic_analyser, array_in_map) { test("struct MyStruct { int x[2]; int y[4]; } " "kprobe:f { @ = ((struct MyStruct *)arg0)->x; }"); test("struct MyStruct { int x[2]; int y[4]; } " "kprobe:f { @a[0] = ((struct MyStruct *)arg0)->x; }"); // Mismatched map value types test("struct MyStruct { int x[2]; int y[4]; } " "kprobe:f { " " @a[0] = ((struct MyStruct *)arg0)->x; " " @a[1] = ((struct MyStruct *)arg0)->y; " "}", 1); test("#include \n" "struct MyStruct { uint8_t x[8]; uint32_t y[2]; }" "kprobe:f { " " @a[0] = ((struct MyStruct *)arg0)->x; " " @a[1] = ((struct MyStruct *)arg0)->y; " "}", 1); } TEST(semantic_analyser, array_as_map_key) { test("struct MyStruct { int x[2]; int y[4]; }" "kprobe:f { @x[((struct MyStruct *)arg0)->x] = 0; }"); test("struct MyStruct { int x[2]; int y[4]; }" "kprobe:f { @x[((struct MyStruct *)arg0)->x, " " ((struct MyStruct *)arg0)->y] = 0; }"); // Mismatched key types test_error(R"( struct MyStruct { int x[2]; int y[4]; } BEGIN { @x[((struct MyStruct *)0)->x] = 0; @x[((struct MyStruct *)0)->y] = 1; })", R"( stdin:4:7-37: ERROR: Argument mismatch for @x: trying to access with arguments: 'int32[4]' when map expects arguments: 'int32[2]' @x[((struct MyStruct *)0)->y] = 1; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(semantic_analyser, array_compare) { test("#include \n" "struct MyStruct { uint8_t x[4]; }" "kprobe:f { $s = (struct MyStruct *) arg0; @ = ($s->x == $s->x); }"); test("#include \n" "struct MyStruct { uint64_t x[4]; } " "kprobe:f { $s = (struct MyStruct *) arg0; @ = ($s->x == $s->x); }"); test("struct MyStruct { int x[4]; } " "kprobe:f { $s = (struct MyStruct *) arg0; @ = ($s->x != $s->x); }"); // unsupported operators test("struct MyStruct { int x[4]; } " "kprobe:f { $s = (struct MyStruct *) arg0; @ = ($s->x > $s->x); }", 3); // different length test("struct MyStruct { int x[4]; int y[8]; }" "kprobe:f { $s = (struct MyStruct *) arg0; @ = ($s->x == $s->y); }", 3); // different element type test("#include \n" "struct MyStruct { uint8_t x[4]; uint16_t y[4]; } " "kprobe:f { $s = (struct MyStruct *) arg0; @ = ($s->x == $s->y); }", 3); // compare with other type test("struct MyStruct { int x[4]; int y; } " "kprobe:f { $s = (struct MyStruct *) arg0; @ = ($s->x == $s->y); }", 3); } TEST(semantic_analyser, variable_type) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $x = 1 }"); auto st = CreateInt64(); auto assignment = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(0)); EXPECT_EQ(st, assignment->var->type); } TEST(semantic_analyser, unroll) { test("kprobe:f { $i = 0; unroll(5) { printf(\"i: %d\\n\", $i); $i = $i + 1; " "} }"); test("kprobe:f { $i = 0; unroll(101) { printf(\"i: %d\\n\", $i); $i = $i + " "1; } }", 1); test("kprobe:f { $i = 0; unroll(0) { printf(\"i: %d\\n\", $i); $i = $i + 1; " "} }", 1); BPFtrace bpftrace; bpftrace.add_param("10"); bpftrace.add_param("hello"); bpftrace.add_param("101"); test(bpftrace, R"(kprobe:f { unroll($#) { printf("hi\n"); } })"); test(bpftrace, R"(kprobe:f { unroll($1) { printf("hi\n"); } })"); test(bpftrace, R"(kprobe:f { unroll($2) { printf("hi\n"); } })", 1); test(bpftrace, R"(kprobe:f { unroll($3) { printf("hi\n"); } })", 1); } TEST(semantic_analyser, map_integer_sizes) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $x = (int32) -1; @x = $x; }"); auto var_assignment = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(0)); auto map_assignment = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(1)); EXPECT_EQ(CreateInt32(), var_assignment->var->type); EXPECT_EQ(CreateInt64(), map_assignment->map->type); } TEST(semantic_analyser, binop_integer_promotion) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $x = (int32)5 + (int16)6 }"); auto var_assignment = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(0)); EXPECT_EQ(CreateInt32(), var_assignment->var->type); } TEST(semantic_analyser, binop_integer_no_promotion) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $x = (int8)5 + (int8)6 }"); auto var_assignment = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(0)); EXPECT_EQ(CreateInt8(), var_assignment->var->type); } TEST(semantic_analyser, unop_dereference) { test("kprobe:f { *0; }"); test("struct X { int n; } kprobe:f { $x = (struct X*)0; *$x; }"); test("struct X { int n; } kprobe:f { $x = *(struct X*)0; *$x; }", 1); test("kprobe:f { *\"0\"; }", 2); } TEST(semantic_analyser, unop_not) { std::string structs = "struct X { int x; };"; test("kprobe:f { ~0; }"); test(structs + "kprobe:f { $x = *(struct X*)0; ~$x; }", 2); test(structs + "kprobe:f { $x = (struct X*)0; ~$x; }", 2); test("kprobe:f { ~\"0\"; }", 2); } TEST(semantic_analyser, unop_lnot) { test("kprobe:f { !0; }"); test("kprobe:f { !(int32)0; }"); test("struct X { int n; } kprobe:f { $x = (struct X*)0; !$x; }", 2); test("struct X { int n; } kprobe:f { $x = *(struct X*)0; !$x; }", 2); test("kprobe:f { !\"0\"; }", 2); } TEST(semantic_analyser, unop_increment_decrement) { test("kprobe:f { $x = 0; $x++; }"); test("kprobe:f { $x = 0; $x--; }"); test("kprobe:f { $x = 0; ++$x; }"); test("kprobe:f { $x = 0; --$x; }"); test("kprobe:f { @x++; }"); test("kprobe:f { @x--; }"); test("kprobe:f { ++@x; }"); test("kprobe:f { --@x; }"); test("kprobe:f { $x++; }", 1); test("kprobe:f { @x = \"a\"; @x++; }", 3); // should be 2 test("kprobe:f { $x = \"a\"; $x++; }", 2); } #ifdef HAVE_LIBLLDB #include "dwarf_common.h" class semantic_analyser_dwarf : public test_dwarf {}; TEST_F(semantic_analyser_dwarf, reference_into_deref) { auto uprobe = "uprobe:" + std::string(cxx_bin_) + ":cpp:func_1"; BPFtrace bpftrace; test(bpftrace, uprobe + " { args.c }", R"( Program )" + uprobe + R"( dereference :: [Child, AS(user)] . :: [Child *, AS(user)] builtin: args :: [struct )" + uprobe + R"(_args, ctx: 1, AS(user)] c )"); } #endif // HAVE_LIBLLDB TEST(semantic_analyser, printf) { test("kprobe:f { printf(\"hi\") }"); test("kprobe:f { printf(1234) }", 1); test("kprobe:f { printf() }", 1); test("kprobe:f { $fmt = \"mystring\"; printf($fmt) }", 1); test("kprobe:f { printf(\"%s\", comm) }"); test("kprobe:f { printf(\"%-16s\", comm) }"); test("kprobe:f { printf(\"%-10.10s\", comm) }"); test("kprobe:f { printf(\"%A\", comm) }", 2); test("kprobe:f { @x = printf(\"hi\") }", 1); test("kprobe:f { $x = printf(\"hi\") }", 1); test("kprobe:f { printf(\"%d %d %d %d %d %d %d %d %d\", 1, 2, 3, 4, 5, 6, 7, " "8, 9); }"); test("kprobe:f { printf(\"%dns\", nsecs) }"); { // Long format string should be ok std::stringstream prog; prog << "i:ms:100 { printf(\"" << std::string(200, 'a') << " %d\\n\", 1); }"; test(prog.str()); } } TEST(semantic_analyser, debugf) { test_for_warning( "kprobe:f { debugf(\"warning\") }", "The debugf() builtin is not recommended for production use."); test("kprobe:f { debugf(\"hi\") }"); test("kprobe:f { debugf(1234) }", 1); test("kprobe:f { debugf() }", 1); test("kprobe:f { $fmt = \"mystring\"; debugf($fmt) }", 1); test("kprobe:f { debugf(\"%s\", comm) }"); test("kprobe:f { debugf(\"%-16s\", comm) }"); test("kprobe:f { debugf(\"%-10.10s\", comm) }"); test("kprobe:f { debugf(\"%lluns\", nsecs) }"); test("kprobe:f { debugf(\"%A\", comm) }", 2); test("kprobe:f { @x = debugf(\"hi\") }", 1); test("kprobe:f { $x = debugf(\"hi\") }", 1); test("kprobe:f { debugf(\"%d\", 1) }"); test("kprobe:f { debugf(\"%d %d\", 1, 1) }"); test("kprobe:f { debugf(\"%d %d %d\", 1, 1, 1) }"); test("kprobe:f { debugf(\"%d %d %d %d\", 1, 1, 1, 1) }", 2); { // Long format string should be ok std::stringstream prog; prog << "i:ms:100 { debugf(\"" << std::string(59, 'a') << R"(%s\n", "a"); })"; test(prog.str()); } } TEST(semantic_analyser, system) { test("kprobe:f { system(\"ls\") }", 0, false /* safe_mode */); test("kprobe:f { system(1234) }", 1, false /* safe_mode */); test("kprobe:f { system() }", 1, false /* safe_mode */); test("kprobe:f { $fmt = \"mystring\"; system($fmt) }", 1, false /* safe_mode */); } TEST(semantic_analyser, printf_format_int) { test("kprobe:f { printf(\"int: %d\", 1234) }"); test("kprobe:f { printf(\"int: %d\", pid) }"); test("kprobe:f { @x = 123; printf(\"int: %d\", @x) }"); test("kprobe:f { $x = 123; printf(\"int: %d\", $x) }"); test("kprobe:f { printf(\"int: %u\", 1234) }"); test("kprobe:f { printf(\"int: %o\", 1234) }"); test("kprobe:f { printf(\"int: %x\", 1234) }"); test("kprobe:f { printf(\"int: %X\", 1234) }"); } TEST(semantic_analyser, printf_format_int_with_length) { test("kprobe:f { printf(\"int: %d\", 1234) }"); test("kprobe:f { printf(\"int: %u\", 1234) }"); test("kprobe:f { printf(\"int: %o\", 1234) }"); test("kprobe:f { printf(\"int: %x\", 1234) }"); test("kprobe:f { printf(\"int: %X\", 1234) }"); test("kprobe:f { printf(\"int: %p\", 1234) }"); test("kprobe:f { printf(\"int: %hhd\", 1234) }"); test("kprobe:f { printf(\"int: %hhu\", 1234) }"); test("kprobe:f { printf(\"int: %hho\", 1234) }"); test("kprobe:f { printf(\"int: %hhx\", 1234) }"); test("kprobe:f { printf(\"int: %hhX\", 1234) }"); test("kprobe:f { printf(\"int: %hhp\", 1234) }"); test("kprobe:f { printf(\"int: %hd\", 1234) }"); test("kprobe:f { printf(\"int: %hu\", 1234) }"); test("kprobe:f { printf(\"int: %ho\", 1234) }"); test("kprobe:f { printf(\"int: %hx\", 1234) }"); test("kprobe:f { printf(\"int: %hX\", 1234) }"); test("kprobe:f { printf(\"int: %hp\", 1234) }"); test("kprobe:f { printf(\"int: %ld\", 1234) }"); test("kprobe:f { printf(\"int: %lu\", 1234) }"); test("kprobe:f { printf(\"int: %lo\", 1234) }"); test("kprobe:f { printf(\"int: %lx\", 1234) }"); test("kprobe:f { printf(\"int: %lX\", 1234) }"); test("kprobe:f { printf(\"int: %lp\", 1234) }"); test("kprobe:f { printf(\"int: %lld\", 1234) }"); test("kprobe:f { printf(\"int: %llu\", 1234) }"); test("kprobe:f { printf(\"int: %llo\", 1234) }"); test("kprobe:f { printf(\"int: %llx\", 1234) }"); test("kprobe:f { printf(\"int: %llX\", 1234) }"); test("kprobe:f { printf(\"int: %llp\", 1234) }"); test("kprobe:f { printf(\"int: %jd\", 1234) }"); test("kprobe:f { printf(\"int: %ju\", 1234) }"); test("kprobe:f { printf(\"int: %jo\", 1234) }"); test("kprobe:f { printf(\"int: %jx\", 1234) }"); test("kprobe:f { printf(\"int: %jX\", 1234) }"); test("kprobe:f { printf(\"int: %jp\", 1234) }"); test("kprobe:f { printf(\"int: %zd\", 1234) }"); test("kprobe:f { printf(\"int: %zu\", 1234) }"); test("kprobe:f { printf(\"int: %zo\", 1234) }"); test("kprobe:f { printf(\"int: %zx\", 1234) }"); test("kprobe:f { printf(\"int: %zX\", 1234) }"); test("kprobe:f { printf(\"int: %zp\", 1234) }"); test("kprobe:f { printf(\"int: %td\", 1234) }"); test("kprobe:f { printf(\"int: %tu\", 1234) }"); test("kprobe:f { printf(\"int: %to\", 1234) }"); test("kprobe:f { printf(\"int: %tx\", 1234) }"); test("kprobe:f { printf(\"int: %tX\", 1234) }"); test("kprobe:f { printf(\"int: %tp\", 1234) }"); } TEST(semantic_analyser, printf_format_string) { test(R"(kprobe:f { printf("str: %s", "mystr") })"); test("kprobe:f { printf(\"str: %s\", comm) }"); test("kprobe:f { printf(\"str: %s\", str(arg0)) }"); test(R"(kprobe:f { @x = "hi"; printf("str: %s", @x) })"); test(R"(kprobe:f { $x = "hi"; printf("str: %s", $x) })"); } TEST(semantic_analyser, printf_bad_format_string) { test(R"(kprobe:f { printf("%d", "mystr") })", 2); test("kprobe:f { printf(\"%d\", str(arg0)) }", 2); test("kprobe:f { printf(\"%s\", 1234) }", 2); test("kprobe:f { printf(\"%s\", arg0) }", 2); } TEST(semantic_analyser, printf_format_buf) { test(R"(kprobe:f { printf("%r", buf("mystr", 5)) })"); } TEST(semantic_analyser, printf_bad_format_buf) { test(R"(kprobe:f { printf("%r", "mystr") })", 2); test("kprobe:f { printf(\"%r\", arg0) }", 2); } TEST(semantic_analyser, printf_format_buf_no_ascii) { test(R"(kprobe:f { printf("%rx", buf("mystr", 5)) })"); } TEST(semantic_analyser, printf_bad_format_buf_no_ascii) { test(R"(kprobe:f { printf("%rx", "mystr") })", 2); test("kprobe:f { printf(\"%rx\", arg0) }", 2); } TEST(semantic_analyser, printf_format_buf_nonescaped_hex) { test(R"(kprobe:f { printf("%rh", buf("mystr", 5)) })"); } TEST(semantic_analyser, printf_bad_format_buf_nonescaped_hex) { test(R"(kprobe:f { printf("%rh", "mystr") })", 2); test("kprobe:f { printf(\"%rh\", arg0) }", 2); } TEST(semantic_analyser, printf_format_multi) { test(R"(kprobe:f { printf("%d %d %s", 1, 2, "mystr") })"); test(R"(kprobe:f { printf("%d %s %d", 1, 2, "mystr") })", 2); } TEST(semantic_analyser, join) { test("kprobe:f { join(arg0) }"); test("kprobe:f { printf(\"%s\", join(arg0)) }", 2); test("kprobe:f { join() }", 1); test("kprobe:f { $fmt = \"mystring\"; join($fmt) }", 2); test("kprobe:f { @x = join(arg0) }", 1); test("kprobe:f { $x = join(arg0) }", 1); } TEST(semantic_analyser, join_delimiter) { test("kprobe:f { join(arg0, \",\") }"); test(R"(kprobe:f { printf("%s", join(arg0, ",")) })", 2); test(R"(kprobe:f { $fmt = "mystring"; join($fmt, ",") })", 2); test("kprobe:f { @x = join(arg0, \",\") }", 1); test("kprobe:f { $x = join(arg0, \",\") }", 1); test("kprobe:f { join(arg0, 3) }", 2); } TEST(semantic_analyser, kprobe) { test("kprobe:f { 1 }"); test("kretprobe:f { 1 }"); } TEST(semantic_analyser, uprobe) { test("uprobe:/bin/sh:f { 1 }"); test("u:/bin/sh:f { 1 }"); test("uprobe:/bin/sh:0x10 { 1 }"); test("u:/bin/sh:0x10 { 1 }"); test("uprobe:/bin/sh:f+0x10 { 1 }"); test("u:/bin/sh:f+0x10 { 1 }"); test("uprobe:sh:f { 1 }"); test("uprobe:/bin/sh:cpp:f { 1 }"); test("uprobe:/notexistfile:f { 1 }", 1); test("uprobe:notexistfile:f { 1 }", 1); test("uprobe:/bin/sh:nolang:f { 1 }", 1); test("uretprobe:/bin/sh:f { 1 }"); test("ur:/bin/sh:f { 1 }"); test("uretprobe:sh:f { 1 }"); test("ur:sh:f { 1 }"); test("uretprobe:/bin/sh:0x10 { 1 }"); test("ur:/bin/sh:0x10 { 1 }"); test("uretprobe:/bin/sh:cpp:f { 1 }"); test("uretprobe:/notexistfile:f { 1 }", 1); test("uretprobe:notexistfile:f { 1 }", 1); test("uretprobe:/bin/sh:nolang:f { 1 }", 1); } TEST(semantic_analyser, usdt) { test("usdt:/bin/sh:probe { 1 }"); test("usdt:sh:probe { 1 }"); test("usdt:/bin/sh:namespace:probe { 1 }"); test("usdt:/notexistfile:probe { 1 }", 1); test("usdt:notexistfile:probe { 1 }", 1); } TEST(semantic_analyser, begin_end_probes) { test("BEGIN { 1 }"); test("BEGIN { 1 } BEGIN { 2 }", 2); test("END { 1 }"); test("END { 1 } END { 2 }", 2); } TEST(semantic_analyser, self_probe) { test("self:signal:SIGUSR1 { 1 }"); test_error("self:signal:sighup { 1 }", R"( stdin:1:1-19: ERROR: sighup is not a supported signal self:signal:sighup { 1 } ~~~~~~~~~~~~~~~~~~ )"); test_error("self:keypress:space { 1 }", R"( stdin:1:1-20: ERROR: keypress is not a supported trigger self:keypress:space { 1 } ~~~~~~~~~~~~~~~~~~~ )"); } TEST(semantic_analyser, tracepoint) { test("tracepoint:category:event { 1 }"); } TEST(semantic_analyser, rawtracepoint) { test("rawtracepoint:event { 1 }"); test("rawtracepoint:event { args }", 1); } #if defined(__x86_64__) || defined(__aarch64__) TEST(semantic_analyser, watchpoint_invalid_modes) { auto bpftrace = get_mock_bpftrace(); bpftrace->procmon_ = std::make_unique(123); #if defined(__x86_64__) test(*bpftrace, "watchpoint:0x1234:8:r { 1 }", 1); #elif defined(__aarch64__) test(*bpftrace, "watchpoint:0x1234:8:r { 1 }"); #endif test(*bpftrace, "watchpoint:0x1234:8:rx { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:wx { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:xw { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:rwx { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:xx { 1 }", 1); test(*bpftrace, "watchpoint:0x1234:8:b { 1 }", 1); } TEST(semantic_analyser, watchpoint_absolute) { auto bpftrace = get_mock_bpftrace(); bpftrace->procmon_ = std::make_unique(123); test(*bpftrace, "watchpoint:0x1234:8:rw { 1 }"); test(*bpftrace, "watchpoint:0x1234:9:rw { 1 }", 1); test(*bpftrace, "watchpoint:0x0:8:rw { 1 }", 1); } TEST(semantic_analyser, watchpoint_function) { auto bpftrace = get_mock_bpftrace(); bpftrace->procmon_ = std::make_unique(123); test(*bpftrace, "watchpoint:func1+arg2:8:rw { 1 }"); test(*bpftrace, "w:func1+arg2:8:rw { 1 }"); test(*bpftrace, "w:func1.one_two+arg2:8:rw { 1 }"); test(*bpftrace, "watchpoint:func1+arg99999:8:rw { 1 }", 1); bpftrace->procmon_ = std::make_unique(0); test(*bpftrace, "watchpoint:func1+arg2:8:rw { 1 }", 1); } TEST(semantic_analyser, asyncwatchpoint) { auto bpftrace = get_mock_bpftrace(); bpftrace->procmon_ = std::make_unique(123); test(*bpftrace, "asyncwatchpoint:func1+arg2:8:rw { 1 }"); test(*bpftrace, "aw:func1+arg2:8:rw { 1 }"); test(*bpftrace, "aw:func1.one_two+arg2:8:rw { 1 }"); test(*bpftrace, "asyncwatchpoint:func1+arg99999:8:rw { 1 }", 1); // asyncwatchpoint's may not use absolute addresses test(*bpftrace, "asyncwatchpoint:0x1234:8:rw { 1 }", 1); bpftrace->procmon_ = std::make_unique(0); test(*bpftrace, "watchpoint:func1+arg2:8:rw { 1 }", 1); } #endif // if defined(__x86_64__) || defined(__aarch64__) TEST(semantic_analyser, args_builtin_wrong_use) { test("BEGIN { args.foo }", 1); test("END { args.foo }", 1); test("kprobe:f { args.foo }", 1); test("kretprobe:f { args.foo }", 1); test("uretprobe:/bin/sh/:f { args.foo }", 1); test("profile:ms:1 { args.foo }", 1); test("usdt:sh:probe { args.foo }", 1); test("profile:ms:100 { args.foo }", 1); test("hardware:cache-references:1000000 { args.foo }", 1); test("software:faults:1000 { args.foo }", 1); test("interval:s:1 { args.foo }", 1); } TEST(semantic_analyser, profile) { test("profile:hz:997 { 1 }"); test("profile:s:10 { 1 }"); test("profile:ms:100 { 1 }"); test("profile:us:100 { 1 }"); test("profile:unit:100 { 1 }", 1); } TEST(semantic_analyser, interval) { test("interval:hz:997 { 1 }"); test("interval:s:10 { 1 }"); test("interval:ms:100 { 1 }"); test("interval:us:100 { 1 }"); test("interval:unit:100 { 1 }", 1); } TEST(semantic_analyser, variable_cast_types) { std::string structs = "struct type1 { int field; } struct type2 { int field; }"; test(structs + "kprobe:f { $x = (struct type1*)cpu; $x = (struct type1*)cpu; }"); test(structs + "kprobe:f { $x = (struct type1*)cpu; $x = (struct type2*)cpu; }", 1); } TEST(semantic_analyser, map_cast_types) { std::string structs = "struct type1 { int field; } struct type2 { int field; }"; test(structs + "kprobe:f { @x = *(struct type1*)cpu; @x = *(struct type1*)cpu; }"); test(structs + "kprobe:f { @x = *(struct type1*)cpu; @x = *(struct type2*)cpu; }", 1); } TEST(semantic_analyser, map_aggregations_implicit_cast) { // When assigning an aggregation to a map containing integers, // the aggregation is implicitly cast to an integer. test("kprobe:f { @x = 1; @y = count(); @x = @y; }", R"(* = map: @x :: [int64] (int64) map: @y :: [count_t] *)"); test("kprobe:f { @x = 1; @y = sum(5); @x = @y; }", R"(* = map: @x :: [int64] (int64) map: @y :: [sum_t] *)"); test("kprobe:f { @x = 1; @y = min(5); @x = @y; }", R"(* = map: @x :: [int64] (int64) map: @y :: [min_t] *)"); test("kprobe:f { @x = 1; @y = max(5); @x = @y; }", R"(* = map: @x :: [int64] (int64) map: @y :: [max_t] *)"); test("kprobe:f { @x = 1; @y = avg(5); @x = @y; }", R"(* = map: @x :: [int64] (int64) map: @y :: [avg_t] *)"); // Assigning to a newly declared map requires an explicit cast // to get the value of the aggregation. test("kprobe:f { @x = count(); @y = (uint64)@x; }"); test("kprobe:f { @x = sum(5); @y = (uint64)@x; }"); test("kprobe:f { @x = min(5); @y = (uint64)@x; }"); test("kprobe:f { @x = max(5); @y = (uint64)@x; }"); test("kprobe:f { @x = avg(5); @y = (uint64)@x; }"); // However, if there is no explicit cast, // the assignment is rejected and casting is suggested. test_error("kprobe:f { @y = count(); @x = @y; }", R"( stdin:1:26-33: ERROR: Map value 'count_t' cannot be assigned from one map to another. The function that returns this type must be called directly e.g. `@x = count();`. kprobe:f { @y = count(); @x = @y; } ~~~~~~~ HINT: Add a cast to integer if you want the value of the aggregate, e.g. `@x = (int64)@y;`. )"); test_error("kprobe:f { @y = sum(5); @x = @y; }", R"( stdin:1:25-32: ERROR: Map value 'sum_t' cannot be assigned from one map to another. The function that returns this type must be called directly e.g. `@x = sum(retval);`. kprobe:f { @y = sum(5); @x = @y; } ~~~~~~~ HINT: Add a cast to integer if you want the value of the aggregate, e.g. `@x = (int64)@y;`. )"); test_error("kprobe:f { @y = min(5); @x = @y; }", R"( stdin:1:25-32: ERROR: Map value 'min_t' cannot be assigned from one map to another. The function that returns this type must be called directly e.g. `@x = min(retval);`. kprobe:f { @y = min(5); @x = @y; } ~~~~~~~ HINT: Add a cast to integer if you want the value of the aggregate, e.g. `@x = (int64)@y;`. )"); test_error("kprobe:f { @y = max(5); @x = @y; }", R"( stdin:1:25-32: ERROR: Map value 'max_t' cannot be assigned from one map to another. The function that returns this type must be called directly e.g. `@x = max(retval);`. kprobe:f { @y = max(5); @x = @y; } ~~~~~~~ HINT: Add a cast to integer if you want the value of the aggregate, e.g. `@x = (int64)@y;`. )"); test_error("kprobe:f { @y = avg(5); @x = @y; }", R"( stdin:1:25-32: ERROR: Map value 'avg_t' cannot be assigned from one map to another. The function that returns this type must be called directly e.g. `@x = avg(retval);`. kprobe:f { @y = avg(5); @x = @y; } ~~~~~~~ HINT: Add a cast to integer if you want the value of the aggregate, e.g. `@x = (int64)@y;`. )"); test("kprobe:f { @ = count(); if (@ > 0) { print((1)); } }"); test("kprobe:f { @ = sum(5); if (@ > 0) { print((1)); } }"); test("kprobe:f { @ = min(5); if (@ > 0) { print((1)); } }"); test("kprobe:f { @ = max(5); if (@ > 0) { print((1)); } }"); test("kprobe:f { @ = avg(5); if (@ > 0) { print((1)); } }"); test_error("kprobe:f { @ = hist(5); if (@ > 0) { print((1)); } }", R"( stdin:1:28-34: ERROR: Type mismatch for '>': comparing 'hist_t' with 'int64' kprobe:f { @ = hist(5); if (@ > 0) { print((1)); } } ~~~~~~ )"); test_error("kprobe:f { @ = count(); @ += 5 }", R"( stdin:1:25-26: ERROR: Type mismatch for @: trying to assign value of type 'int64' when map already contains a value of type 'count_t' kprobe:f { @ = count(); @ += 5 } ~ )"); } TEST(semantic_analyser, map_aggregations_explicit_cast) { test("kprobe:f { @ = count(); print((1, (uint16)@)); }"); test("kprobe:f { @ = sum(5); print((1, (uint16)@)); }"); test("kprobe:f { @ = min(5); print((1, (uint16)@)); }"); test("kprobe:f { @ = max(5); print((1, (uint16)@)); }"); test("kprobe:f { @ = avg(5); print((1, (uint16)@)); }"); test_error("kprobe:f { @ = hist(5); print((1, (uint16)@)); }", R"( stdin:1:35-43: ERROR: Cannot cast from "hist_t" to "uint16" kprobe:f { @ = hist(5); print((1, (uint16)@)); } ~~~~~~~~ )"); } TEST(semantic_analyser, variable_casts_are_local) { std::string structs = "struct type1 { int field; } struct type2 { int field; }"; test(structs + "kprobe:f { $x = *(struct type1 *)cpu } " "kprobe:g { $x = *(struct type2 *)cpu; }"); } TEST(semantic_analyser, map_casts_are_global) { std::string structs = "struct type1 { int field; } struct type2 { int field; }"; test(structs + "kprobe:f { @x = *(struct type1 *)cpu }" "kprobe:g { @x = *(struct type2 *)cpu }", 1); } TEST(semantic_analyser, cast_unknown_type) { test_error("BEGIN { (struct faketype *)cpu }", R"( stdin:1:9-29: ERROR: Cannot resolve unknown type "struct faketype" BEGIN { (struct faketype *)cpu } ~~~~~~~~~~~~~~~~~~~~ )"); test_error("BEGIN { (faketype)cpu }", R"( stdin:1:9-19: ERROR: Cannot resolve unknown type "faketype" BEGIN { (faketype)cpu } ~~~~~~~~~~ stdin:1:9-19: ERROR: Cannot cast to "faketype" BEGIN { (faketype)cpu } ~~~~~~~~~~ )"); } TEST(semantic_analyser, cast_c_integers) { // Casting to a C integer type gives a hint with the correct name test_error("BEGIN { (char)cpu }", R"( stdin:1:9-15: ERROR: Cannot resolve unknown type "char" BEGIN { (char)cpu } ~~~~~~ stdin:1:9-15: ERROR: Cannot cast to "char" BEGIN { (char)cpu } ~~~~~~ stdin:1:9-15: HINT: Did you mean "int8"? BEGIN { (char)cpu } ~~~~~~ )"); test_error("BEGIN { (short)cpu }", R"( stdin:1:9-16: ERROR: Cannot resolve unknown type "short" BEGIN { (short)cpu } ~~~~~~~ stdin:1:9-16: ERROR: Cannot cast to "short" BEGIN { (short)cpu } ~~~~~~~ stdin:1:9-16: HINT: Did you mean "int16"? BEGIN { (short)cpu } ~~~~~~~ )"); test_error("BEGIN { (int)cpu }", R"( stdin:1:9-14: ERROR: Cannot resolve unknown type "int" BEGIN { (int)cpu } ~~~~~ stdin:1:9-14: ERROR: Cannot cast to "int" BEGIN { (int)cpu } ~~~~~ stdin:1:9-14: HINT: Did you mean "int32"? BEGIN { (int)cpu } ~~~~~ )"); test_error("BEGIN { (long)cpu }", R"( stdin:1:9-15: ERROR: Cannot resolve unknown type "long" BEGIN { (long)cpu } ~~~~~~ stdin:1:9-15: ERROR: Cannot cast to "long" BEGIN { (long)cpu } ~~~~~~ stdin:1:9-15: HINT: Did you mean "int64"? BEGIN { (long)cpu } ~~~~~~ )"); } TEST(semantic_analyser, cast_struct) { // Casting struct by value is forbidden test_error("struct mytype { int field; }\n" "BEGIN { $s = (struct mytype *)cpu; (uint32)*$s; }", R"( stdin:2:37-45: ERROR: Cannot cast from struct type "struct mytype" BEGIN { $s = (struct mytype *)cpu; (uint32)*$s; } ~~~~~~~~ stdin:2:37-45: ERROR: Cannot cast from "struct mytype" to "uint32" BEGIN { $s = (struct mytype *)cpu; (uint32)*$s; } ~~~~~~~~ )"); test_error("struct mytype { int field; } BEGIN { (struct mytype)cpu }", R"( stdin:1:38-54: ERROR: Cannot cast to "struct mytype" struct mytype { int field; } BEGIN { (struct mytype)cpu } ~~~~~~~~~~~~~~~~ )"); } TEST(semantic_analyser, field_access) { std::string structs = "struct type1 { int field; }"; test(structs + "kprobe:f { $x = *(struct type1*)cpu; $x.field }"); test(structs + "kprobe:f { @x = *(struct type1*)cpu; @x.field }"); test("struct task_struct {int x;} kprobe:f { curtask->x }"); } TEST(semantic_analyser, field_access_wrong_field) { std::string structs = "struct type1 { int field; }"; test(structs + "kprobe:f { ((struct type1 *)cpu)->blah }", 1); test(structs + "kprobe:f { $x = (struct type1 *)cpu; $x->blah }", 1); test(structs + "kprobe:f { @x = (struct type1 *)cpu; @x->blah }", 1); } TEST(semantic_analyser, field_access_wrong_expr) { std::string structs = "struct type1 { int field; }"; test(structs + "kprobe:f { 1234->field }", 2); } TEST(semantic_analyser, field_access_types) { std::string structs = "struct type1 { int field; char mystr[8]; }" "struct type2 { int field; }"; test(structs + "kprobe:f { (*((struct type1*)0)).field == 123 }"); test(structs + "kprobe:f { (*((struct type1*)0)).field == \"abc\" }", 2); test(structs + "kprobe:f { (*((struct type1*)0)).mystr == \"abc\" }"); test(structs + "kprobe:f { (*((struct type1*)0)).mystr == 123 }", 2); test(structs + "kprobe:f { (*((struct type1*)0)).field == (*((struct " "type2*)0)).field }"); test(structs + "kprobe:f { (*((struct type1*)0)).mystr == (*((struct " "type2*)0)).field }", 2); } TEST(semantic_analyser, field_access_pointer) { std::string structs = "struct type1 { int field; }"; test(structs + "kprobe:f { ((struct type1*)0)->field }"); test(structs + "kprobe:f { ((struct type1*)0).field }", 1); test(structs + "kprobe:f { *((struct type1*)0) }"); } TEST(semantic_analyser, field_access_sub_struct) { std::string structs = "struct type2 { int field; } " "struct type1 { struct type2 *type2ptr; struct type2 type2; }"; test(structs + "kprobe:f { (*(struct type1*)0).type2ptr->field }"); test(structs + "kprobe:f { (*(struct type1*)0).type2.field }"); test(structs + "kprobe:f { $x = *(struct type2*)0; $x = (*(struct type1*)0).type2 }"); test(structs + "kprobe:f { $x = (struct type2*)0; $x = (*(struct " "type1*)0).type2ptr }"); test( structs + "kprobe:f { $x = *(struct type1*)0; $x = (*(struct type1*)0).type2 }", 1); test(structs + "kprobe:f { $x = (struct type1*)0; $x = (*(struct " "type1*)0).type2ptr }", 1); } TEST(semantic_analyser, field_access_is_internal) { BPFtrace bpftrace; Driver driver(bpftrace); std::string structs = "struct type1 { int x; }"; { test(driver, structs + "kprobe:f { $x = (*(struct type1*)0).x }"); auto &stmts = driver.ctx.root->probes.at(0)->block->stmts; auto var_assignment1 = static_cast(stmts.at(0)); EXPECT_FALSE(var_assignment1->var->type.is_internal); } { test(driver, structs + "kprobe:f { @type1 = *(struct type1*)0; $x = @type1.x }"); auto &stmts = driver.ctx.root->probes.at(0)->block->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); EXPECT_TRUE(var_assignment2->var->type.is_internal); } } TEST(semantic_analyser, struct_as_map_key) { test("struct A { int x; } struct B { char x; } " "kprobe:f { @x[*((struct A *)arg0)] = 0; }"); test("struct A { int x; } struct B { char x; } " "kprobe:f { @x[*((struct A *)arg0), *((struct B *)arg1)] = 0; }"); // Mismatched key types test_error(R"( struct A { int x; } struct B { char x; } BEGIN { @x[*((struct A *)0)] = 0; @x[*((struct B *)0)] = 1; })", R"( stdin:4:9-30: ERROR: Argument mismatch for @x: trying to access with arguments: 'struct B' when map expects arguments: 'struct A' @x[*((struct B *)0)] = 1; ~~~~~~~~~~~~~~~~~~~~~ )"); } TEST(semantic_analyser, per_cpu_map_as_map_key) { test("BEGIN { @x = count(); @y[@x] = 1; }"); test("BEGIN { @x = sum(10); @y[@x] = 1; }"); test("BEGIN { @x = min(1); @y[@x] = 1; }"); test("BEGIN { @x = max(1); @y[@x] = 1; }"); test("BEGIN { @x = avg(1); @y[@x] = 1; }"); test_error("BEGIN { @x = hist(10); @y[@x] = 1; }", R"( stdin:1:24-29: ERROR: hist_t cannot be used as a map key BEGIN { @x = hist(10); @y[@x] = 1; } ~~~~~ )"); test_error("BEGIN { @x = lhist(10, 0, 10, 1); @y[@x] = 1; }", R"( stdin:1:35-40: ERROR: lhist_t cannot be used as a map key BEGIN { @x = lhist(10, 0, 10, 1); @y[@x] = 1; } ~~~~~ )"); test_error("BEGIN { @x = stats(10); @y[@x] = 1; }", R"( stdin:1:25-30: ERROR: stats_t cannot be used as a map key BEGIN { @x = stats(10); @y[@x] = 1; } ~~~~~ )"); } TEST(semantic_analyser, probe_short_name) { test("t:a:b { args }"); test("k:f { pid }"); test("kr:f { pid }"); test("u:sh:f { 1 }"); test("ur:sh:f { 1 }"); test("p:hz:997 { 1 }"); test("h:cache-references:1000000 { 1 }"); test("s:faults:1000 { 1 }"); test("i:s:1 { 1 }"); } TEST(semantic_analyser, positional_parameters) { BPFtrace bpftrace; bpftrace.add_param("123"); bpftrace.add_param("hello"); bpftrace.add_param("0x123"); test(bpftrace, "kprobe:f { printf(\"%d\", $1); }"); test(bpftrace, "kprobe:f { printf(\"%s\", str($1)); }"); test(bpftrace, "kprobe:f { printf(\"%s\", str($2)); }"); test(bpftrace, "kprobe:f { printf(\"%s\", str($2 + 1)); }"); test(bpftrace, "kprobe:f { printf(\"%d\", $2); }", 2); test(bpftrace, "kprobe:f { printf(\"%d\", $3); }"); // Pointer arithmetic in str() for parameters // Only str($1 + CONST) where CONST <= strlen($1) should be allowed test(bpftrace, "kprobe:f { printf(\"%s\", str($1 + 1)); }"); test(bpftrace, "kprobe:f { printf(\"%s\", str(1 + $1)); }"); test(bpftrace, "kprobe:f { printf(\"%s\", str($1 + 4)); }", 2); test(bpftrace, "kprobe:f { printf(\"%s\", str($1 * 2)); }", 2); test(bpftrace, "kprobe:f { printf(\"%s\", str($1 + 1 + 1)); }", 1); // Parameters are not required to exist to be used: test(bpftrace, "kprobe:f { printf(\"%s\", str($4)); }"); test(bpftrace, "kprobe:f { printf(\"%d\", $4); }"); test(bpftrace, "kprobe:f { printf(\"%d\", $#); }"); test(bpftrace, "kprobe:f { printf(\"%s\", str($#)); }", 1); test(bpftrace, "kprobe:f { printf(\"%s\", str($#+1)); }", 1); // Parameters can be used as string literals test(bpftrace, "kprobe:f { printf(\"%d\", cgroupid(str($2))); }"); Driver driver(bpftrace); test(driver, "k:f { $1 }"); auto stmt = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(0)); auto pp = static_cast(stmt->expr); EXPECT_EQ(CreateUInt64(), pp->type); EXPECT_TRUE(pp->is_literal); bpftrace.add_param("0999"); test(bpftrace, "kprobe:f { printf(\"%d\", $4); }", 2); } TEST(semantic_analyser, macros) { test("#define A 1\nkprobe:f { printf(\"%d\", A); }"); test("#define A A\nkprobe:f { printf(\"%d\", A); }", 1); test("enum { A = 1 }\n#define A A\nkprobe:f { printf(\"%d\", A); }"); } TEST(semantic_analyser, enums) { // Anonymous enums have empty string names in libclang <= 15, // so this is an important test test("enum { a = 1, b } kprobe:f { printf(\"%d\", a); }"); test("enum { a = 1, b } kprobe:f { printf(\"%s\", a); }"); test("enum { a = 1, b } kprobe:f { $e = a; printf(\"%s\", $e); }"); test("enum { a = 1, b } kprobe:f { printf(\"%15s %-15s\", a, a); }"); test("enum named { a = 1, b } kprobe:f { printf(\"%d\", a); }"); test("enum named { a = 1, b } kprobe:f { printf(\"%s\", a); }"); test("enum named { a = 1, b } kprobe:f { $e = a; printf(\"%s\", $e); }"); test("enum named { a = 1, b } kprobe:f { printf(\"%15s %-15s\", a, a); }"); // Cannot symbolize a non-enum test_error("kprobe:f { $x = (uint8)1; printf(\"%s\", $x) }", R"( stdin:1:27-43: ERROR: printf: %s specifier expects a value of type string (int supplied) kprobe:f { $x = (uint8)1; printf("%s", $x) } ~~~~~~~~~~~~~~~~ )"); } TEST(semantic_analyser, enum_casts) { test("enum named { a = 1, b } kprobe:f { print((enum named)1); }"); // We can't detect this issue because the cast expr is not a literal test("enum named { a = 1, b } kprobe:f { $x = 3; print((enum named)$x); }"); test_error("enum named { a = 1, b } kprobe:f { print((enum named)3); }", R"( stdin:1:36-56: ERROR: Enum: named doesn't contain a variant value of 3 enum named { a = 1, b } kprobe:f { print((enum named)3); } ~~~~~~~~~~~~~~~~~~~~ )"); test_error("enum Foo { a = 1, b } kprobe:f { print((enum Bar)1); }", R"( stdin:1:34-51: ERROR: Unknown enum: Bar enum Foo { a = 1, b } kprobe:f { print((enum Bar)1); } ~~~~~~~~~~~~~~~~~ )"); test_error("enum named { a = 1, b } kprobe:f { $a = \"str\"; print((enum " "named)$a); }", R"( stdin:1:48-67: ERROR: Cannot cast from "string[4]" to "enum named" enum named { a = 1, b } kprobe:f { $a = "str"; print((enum named)$a); } ~~~~~~~~~~~~~~~~~~~ )"); } TEST(semantic_analyser, signed_int_comparison_warnings) { bool invert = true; std::string cmp_sign = "comparison of integers of different signs"; test_for_warning("kretprobe:f /-1 < retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 > retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 >= retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 <= retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 != retval/ {}", cmp_sign); test_for_warning("kretprobe:f /-1 == retval/ {}", cmp_sign); test_for_warning("kretprobe:f /retval > -1/ {}", cmp_sign); test_for_warning("kretprobe:f /retval < -1/ {}", cmp_sign); // These should not trigger a warning test_for_warning("kretprobe:f /1 < retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 > retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 >= retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 <= retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 != retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /1 == retval/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /retval > 1/ {}", cmp_sign, invert); test_for_warning("kretprobe:f /retval < 1/ {}", cmp_sign, invert); } TEST(semantic_analyser, string_comparison) { test("struct MyStruct {char y[4]; } kprobe:f { $s = (struct MyStruct*)arg0; " "$s->y == \"abc\"}"); test("struct MyStruct {char y[4]; } kprobe:f { $s = (struct MyStruct*)arg0; " "\"abc\" != $s->y}"); test("struct MyStruct {char y[4]; } kprobe:f { $s = (struct MyStruct*)arg0; " "\"abc\" == \"abc\"}"); bool invert = true; std::string msg = "the condition is always false"; test_for_warning("struct MyStruct {char y[4]; } kprobe:f { $s = (struct " "MyStruct*)arg0; $s->y == \"long string\"}", msg, invert); test_for_warning("struct MyStruct {char y[4]; } kprobe:f { $s = (struct " "MyStruct*)arg0; \"long string\" != $s->y}", msg, invert); } TEST(semantic_analyser, signed_int_arithmetic_warnings) { // Test type warnings for arithmetic bool invert = true; std::string msg = "arithmetic on integers of different signs"; test_for_warning("kprobe:f { @ = -1 - arg0 }", msg); test_for_warning("kprobe:f { @ = -1 + arg0 }", msg); test_for_warning("kprobe:f { @ = -1 * arg0 }", msg); test_for_warning("kprobe:f { @ = -1 / arg0 }", msg); test_for_warning("kprobe:f { @ = arg0 + 1 }", msg, invert); test_for_warning("kprobe:f { @ = arg0 - 1 }", msg, invert); test_for_warning("kprobe:f { @ = arg0 * 1 }", msg, invert); test_for_warning("kprobe:f { @ = arg0 / 1 }", msg, invert); } TEST(semantic_analyser, signed_int_division_warnings) { bool invert = true; std::string msg = "signed operands"; test_for_warning("kprobe:f { @ = -1 / 1 }", msg); test_for_warning("kprobe:f { @ = 1 / -1 }", msg); // These should not trigger a warning test_for_warning("kprobe:f { @ = 1 / 1 }", msg, invert); test_for_warning("kprobe:f { @ = -(1 / 1) }", msg, invert); } TEST(semantic_analyser, signed_int_modulo_warnings) { bool invert = true; std::string msg = "signed operands"; test_for_warning("kprobe:f { @ = -1 % 1 }", msg); test_for_warning("kprobe:f { @ = 1 % -1 }", msg); // These should not trigger a warning test_for_warning("kprobe:f { @ = 1 % 1 }", msg, invert); test_for_warning("kprobe:f { @ = -(1 % 1) }", msg, invert); } TEST(semantic_analyser, map_as_lookup_table) { // Initializing a map should not lead to usage issues test("BEGIN { @[0] = \"abc\"; @[1] = \"def\" } kretprobe:f { " "printf(\"%s\\n\", @[retval])}"); } TEST(semantic_analyser, cast_sign) { // The C struct parser should set the is_signed flag on signed types BPFtrace bpftrace; Driver driver(bpftrace); std::string prog = "struct t { int s; unsigned int us; long l; unsigned long ul }; " "kprobe:f { " " $t = ((struct t *)0xFF);" " $s = $t->s; $us = $t->us; $l = $t->l; $lu = $t->ul; }"; test(driver, prog); auto s = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(1)); auto us = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(2)); auto l = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(3)); auto ul = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(4)); EXPECT_EQ(CreateInt32(), s->var->type); EXPECT_EQ(CreateUInt32(), us->var->type); EXPECT_EQ(CreateInt64(), l->var->type); EXPECT_EQ(CreateUInt64(), ul->var->type); } TEST(semantic_analyser, binop_sign) { // Make sure types are correct std::string prog_pre = "struct t { long l; unsigned long ul }; " "kprobe:f { " " $t = ((struct t *)0xFF); "; std::string operators[] = { "==", "!=", "<", "<=", ">", ">=", "+", "-", "/", "*" }; for (std::string op : operators) { BPFtrace bpftrace; Driver driver(bpftrace); std::string prog = prog_pre + "$varA = $t->l " + op + " $t->l; " "$varB = $t->ul " + op + " $t->l; " "$varC = $t->ul " + op + " $t->ul;" "}"; test(driver, prog); auto varA = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(1)); EXPECT_EQ(CreateInt64(), varA->var->type); auto varB = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(2)); EXPECT_EQ(CreateUInt64(), varB->var->type); auto varC = static_cast( driver.ctx.root->probes.at(0)->block->stmts.at(3)); EXPECT_EQ(CreateUInt64(), varC->var->type); } } TEST(semantic_analyser, int_cast_types) { test("kretprobe:f { @ = (int8)retval }"); test("kretprobe:f { @ = (int16)retval }"); test("kretprobe:f { @ = (int32)retval }"); test("kretprobe:f { @ = (int64)retval }"); test("kretprobe:f { @ = (uint8)retval }"); test("kretprobe:f { @ = (uint16)retval }"); test("kretprobe:f { @ = (uint32)retval }"); test("kretprobe:f { @ = (uint64)retval }"); } TEST(semantic_analyser, int_cast_usage) { test("kretprobe:f /(int32) retval < 0 / {}"); test("kprobe:f /(int32) arg0 < 0 / {}"); test("kprobe:f { @=sum((int32)arg0) }"); test("kprobe:f { @=avg((int32)arg0) }"); test("kprobe:f { @=avg((int32)arg0) }"); test("kprobe:f { @=avg((int32)\"abc\") }", 1); } TEST(semantic_analyser, intptr_cast_types) { test("kretprobe:f { @ = *(int8*)retval }"); test("kretprobe:f { @ = *(int16*)retval }"); test("kretprobe:f { @ = *(int32*)retval }"); test("kretprobe:f { @ = *(int64*)retval }"); test("kretprobe:f { @ = *(uint8*)retval }"); test("kretprobe:f { @ = *(uint16*)retval }"); test("kretprobe:f { @ = *(uint32*)retval }"); test("kretprobe:f { @ = *(uint64*)retval }"); } TEST(semantic_analyser, intptr_cast_usage) { test("kretprobe:f /(*(int32*) retval) < 0 / {}"); test("kprobe:f /(*(int32*) arg0) < 0 / {}"); test("kprobe:f { @=sum(*(int32*)arg0) }"); test("kprobe:f { @=avg(*(int32*)arg0) }"); test("kprobe:f { @=avg(*(int32*)arg0) }"); // This is OK (@ = 0x636261) test("kprobe:f { @=avg(*(int32*)\"abc\") }"); test("kprobe:f { @=avg(*(int32*)123) }"); } TEST(semantic_analyser, intarray_cast_types) { test("kprobe:f { @ = (int8[8])1 }"); test("kprobe:f { @ = (int16[4])1 }"); test("kprobe:f { @ = (int32[2])1 }"); test("kprobe:f { @ = (int64[1])1 }"); test("kprobe:f { @ = (int8[4])(int32)1 }"); test("kprobe:f { @ = (int8[2])(int16)1 }"); test("kprobe:f { @ = (int8[1])(int8)1 }"); test("kprobe:f { @ = (int8[])1 }"); test("kprobe:f { @ = (uint8[8])1 }"); test("kretprobe:f { @ = (int8[8])retval }"); test("kprobe:f { @ = (int8[4])1 }", 1); test("kprobe:f { @ = (bool[64])1 }", 1); test("kprobe:f { @ = (int32[])(int16)1 }", 1); test("kprobe:f { @ = (int8[6])\"hello\" }", 1); test("struct Foo { int x; } kprobe:f { @ = (struct Foo [2])1 }", 1); } TEST(semantic_analyser, intarray_cast_usage) { test("kprobe:f { $a=(int8[8])1; }"); test("kprobe:f { @=(int8[8])1; }"); test("kprobe:f { @[(int8[8])1] = 0; }"); test("kprobe:f { if (((int8[8])1)[0] == 1) {} }"); } TEST(semantic_analyser, intarray_to_int_cast) { test("#include \n" "struct Foo { uint8_t x[8]; } " "kprobe:f { @ = (int64)((struct Foo *)arg0)->x; }"); test("#include \n" "struct Foo { uint32_t x[2]; } " "kprobe:f { @ = (int64)((struct Foo *)arg0)->x; }"); test("#include \n" "struct Foo { uint8_t x[4]; } " "kprobe:f { @ = (int32)((struct Foo *)arg0)->x; }"); test("#include \n" "struct Foo { uint8_t x[8]; } " "kprobe:f { @ = (int32)((struct Foo *)arg0)->x; }", 1); test("#include \n" "struct Foo { uint8_t x[8]; } " "kprobe:f { @ = (int32 *)((struct Foo *)arg0)->x; }", 1); } TEST(semantic_analyser, mixed_int_var_assignments) { test("kprobe:f { $x = (uint64)0; $x = (uint16)1; }"); test("kprobe:f { $x = (int8)1; $x = 5; }"); test("kprobe:f { $x = 1; $x = -1; }"); test("kprobe:f { $x = (uint8)1; $x = 200; }"); test("kprobe:f { $x = (int8)1; $x = -2; }"); test("kprobe:f { $x = (int16)1; $x = 20000; }"); // We'd like the below to work, but blocked on #3518. // TLDR: It looks like a literal and thus amenable to static "fits into" // checks. But it's not, the parser has actually desugared it to: // AssignVarStatement(Variable, Binop(Variable, Integer(1))) // test("kprobe:f { $x = (uint32)5; $x += 1; }"); test_error("kprobe:f { $x = (uint8)1; $x = -1; }", R"( stdin:1:27-34: ERROR: Type mismatch for $x: trying to assign value of type 'int64' when variable already contains a value of type 'uint8' kprobe:f { $x = (uint8)1; $x = -1; } ~~~~~~~ )"); test_error("kprobe:f { $x = (int16)1; $x = 100000; }", R"( stdin:1:27-38: ERROR: Type mismatch for $x: trying to assign value '100000' which does not fit into the variable of type 'int16' kprobe:f { $x = (int16)1; $x = 100000; } ~~~~~~~~~~~ )"); test_error("kprobe:f { $a = (uint16)5; $x = (uint8)0; $x = $a; }", R"( stdin:1:43-50: ERROR: Integer size mismatch. Assignment type 'uint16' is larger than the variable type 'uint8'. kprobe:f { $a = (uint16)5; $x = (uint8)0; $x = $a; } ~~~~~~~ )"); test_error("kprobe:f { $a = (int8)-1; $x = (uint8)0; $x = $a; }", R"( stdin:1:42-49: ERROR: Type mismatch for $x: trying to assign value of type 'int8' when variable already contains a value of type 'uint8' kprobe:f { $a = (int8)-1; $x = (uint8)0; $x = $a; } ~~~~~~~ )"); test_error("kprobe:f { $x = -1; $x = 10223372036854775807; }", R"( stdin:1:21-46: ERROR: Type mismatch for $x: trying to assign value '10223372036854775807' which does not fit into the variable of type 'int64' kprobe:f { $x = -1; $x = 10223372036854775807; } ~~~~~~~~~~~~~~~~~~~~~~~~~ )"); test_error("kprobe:f { $x = (0, (uint32)123); $x = (0, (int32)-123); }", R"( stdin:1:35-56: ERROR: Type mismatch for $x: trying to assign value of type '(int64,int32)' when variable already contains a value of type '(int64,uint32)' kprobe:f { $x = (0, (uint32)123); $x = (0, (int32)-123); } ~~~~~~~~~~~~~~~~~~~~~ )"); test("BEGIN { $x = (uint8)1; $x = 5; }", R"( Program BEGIN = variable: $x :: [uint8] (uint8) int: 1 :: [int64] = variable: $x :: [uint8] (uint8) int: 5 :: [int64] )"); test("BEGIN { $x = (int8)1; $x = 5; }", R"( Program BEGIN = variable: $x :: [int8] (int8) int: 1 :: [int64] = variable: $x :: [int8] (int8) int: 5 :: [int64] )"); } TEST(semantic_analyser, signal) { // int literals test("k:f { signal(1); }", 0, false); test("kr:f { signal(1); }", 0, false); test("u:/bin/sh:f { signal(11); }", 0, false); test("ur:/bin/sh:f { signal(11); }", 0, false); test("p:hz:1 { signal(1); }", 0, false); // vars test("k:f { @=1; signal(@); }", 0, false); test("k:f { @=1; signal((int32)arg0); }", 0, false); // String test("k:f { signal(\"KILL\"); }", 0, false); test("k:f { signal(\"SIGKILL\"); }", 0, false); // Not allowed for: test("hardware:pcm:1000 { signal(1); }", 1, false); test("software:pcm:1000 { signal(1); }", 1, false); test("BEGIN { signal(1); }", 1, false); test("END { signal(1); }", 1, false); test("i:s:1 { signal(1); }", 1, false); // invalid signals test("k:f { signal(0); }", 1, false); test("k:f { signal(-100); }", 1, false); test("k:f { signal(100); }", 1, false); test("k:f { signal(\"SIGABC\"); }", 1, false); test("k:f { signal(\"ABC\"); }", 1, false); // Missing kernel support MockBPFfeature feature(false); test(feature, "k:f { signal(1) }", 1, false); test(feature, "k:f { signal(\"KILL\"); }", 1, false); // Positional parameter BPFtrace bpftrace; bpftrace.add_param("1"); bpftrace.add_param("hello"); test(bpftrace, "k:f { signal($1) }", false); test(bpftrace, "k:f { signal($2) }", 1, false); } TEST(semantic_analyser, strncmp) { // Test strncmp builtin test(R"(i:s:1 { $a = "bar"; strncmp("foo", $a, 1) })"); test(R"(i:s:1 { strncmp("foo", "bar", 1) })"); test("i:s:1 { strncmp(1) }", 1); test("i:s:1 { strncmp(1,1,1) }", 2); test("i:s:1 { strncmp(\"a\",1,1) }", 2); test(R"(i:s:1 { strncmp("a","a",-1) })", 1); test(R"(i:s:1 { strncmp("a","a","foo") })", 1); } TEST(semantic_analyser, strncmp_posparam) { BPFtrace bpftrace; bpftrace.add_param("1"); bpftrace.add_param("hello"); test(bpftrace, R"(i:s:1 { strncmp("foo", "bar", $1) })"); test(bpftrace, R"(i:s:1 { strncmp("foo", "bar", $2) })", 1); } TEST(semantic_analyser, strcontains) { // Test strcontains builtin test(R"(i:s:1 { $a = "bar"; strcontains("foo", $a) })"); test(R"(i:s:1 { strcontains("foo", "bar") })"); test("i:s:1 { strcontains(1) }", 1); test("i:s:1 { strcontains(1,1) }", 2); test("i:s:1 { strcontains(\"a\",1) }", 2); } TEST(semantic_analyser, strcontains_large_warnings) { test_for_warning( "k:f { $s1 = str(arg0); $s2 = str(arg1); strcontains($s1, $s2) }", "both string sizes is larger"); test_for_warning( "k:f { $s1 = str(arg0, 64); $s2 = str(arg1, 16); strcontains($s1, $s2) }", "both string sizes is larger", /* invert= */ true); auto bpftrace = get_mock_bpftrace(); ConfigSetter configs{ bpftrace->config_, ConfigSource::script }; configs.set(ConfigKeyInt::max_strlen, 16); test_for_warning( *bpftrace, "k:f { $s1 = str(arg0); $s2 = str(arg1); strcontains($s1, $s2) }", "both string sizes is larger", /* invert= */ true); } TEST(semantic_analyser, strcontains_posparam) { BPFtrace bpftrace; bpftrace.add_param("hello"); test(bpftrace, "i:s:1 { strcontains(\"foo\", str($1)) }"); } TEST(semantic_analyser, override) { // literals test("k:f { override(-1); }", 0, false); // variables test("k:f { override(arg0); }", 0, false); // Probe types test("kr:f { override(-1); }", 1, false); test("u:/bin/sh:f { override(-1); }", 1, false); test("t:syscalls:sys_enter_openat { override(-1); }", 1, false); test("i:s:1 { override(-1); }", 1, false); test("p:hz:1 { override(-1); }", 1, false); } TEST(semantic_analyser, unwatch) { test("i:s:1 { unwatch(12345) }"); test("i:s:1 { unwatch(0x1234) }"); test("i:s:1 { $x = 1; unwatch($x); }"); test("i:s:1 { @x = 1; @x++; unwatch(@x); }"); test("k:f { unwatch(arg0); }"); test("k:f { unwatch((int64)arg0); }"); test("k:f { unwatch(*(int64*)arg0); }"); test("i:s:1 { unwatch(\"asdf\") }", 2); test(R"(i:s:1 { @x["hi"] = "world"; unwatch(@x["hi"]) })", 3); test("i:s:1 { printf(\"%d\", unwatch(2)) }", 2); } TEST(semantic_analyser, struct_member_keywords) { std::string keywords[] = { "arg0", "args", "curtask", "func", "gid" "rand", "uid", "avg", "cat", "exit", "kaddr", "min", "printf", "usym", "kstack", "ustack", "bpftrace", "perf", "raw", "uprobe", "kprobe", "config", "fn", }; for (auto kw : keywords) { test("struct S{ int " + kw + ";}; k:f { ((struct S*)arg0)->" + kw + "}"); test("struct S{ int " + kw + ";}; k:f { (*(struct S*)arg0)." + kw + "}"); } } TEST(semantic_analyser, jumps) { test("i:s:1 { return; }"); // must be used in loops test("i:s:1 { break; }", 1); test("i:s:1 { continue; }", 1); } TEST(semantic_analyser, while_loop) { test("i:s:1 { $a = 1; while ($a < 10) { $a++ }}"); test("i:s:1 { $a = 1; while (1) { if($a > 50) { break } $a++ }}"); test("i:s:1 { $a = 1; while ($a < 10) { $a++ }}"); test("i:s:1 { $a = 1; while (1) { if($a > 50) { break } $a++ }}"); test("i:s:1 { $a = 1; while (1) { if($a > 50) { return } $a++ }}"); test(R"PROG( i:s:1 { $a = 1; while ($a < 10) { $a++; $j=0; while ($j < 10) { $j++; } } })PROG"); test_for_warning("i:s:1 { $a = 1; while ($a < 10) { break; $a++ }}", "code after a 'break'"); test_for_warning("i:s:1 { $a = 1; while ($a < 10) { continue; $a++ }}", "code after a 'continue'"); test_for_warning("i:s:1 { $a = 1; while ($a < 10) { return; $a++ }}", "code after a 'return'"); test_for_warning("i:s:1 { $a = 1; while ($a < 10) { @=$a++; print(@); }}", "'print()' in a loop"); } TEST(semantic_analyser, builtin_args) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, "t:sched:sched_one { args.common_field }"); test(*bpftrace, "t:sched:sched_two { args.common_field }"); test(*bpftrace, "t:sched:sched_one," "t:sched:sched_two { args.common_field }"); test(*bpftrace, "t:sched:sched_* { args.common_field }"); test(*bpftrace, "t:sched:sched_one { args.not_a_field }", 1); // Backwards compatibility test(*bpftrace, "t:sched:sched_one { args->common_field }"); } TEST(semantic_analyser, type_ctx) { BPFtrace bpftrace; Driver driver(bpftrace); std::string structs = "struct c {char c} struct x { long a; short b[4]; " "struct c c; struct c *d;}"; test(driver, structs + "kprobe:f { $x = (struct x*)ctx; $a = $x->a; $b = $x->b[0]; " "$c = $x->c.c; $d = $x->d->c;}"); auto &stmts = driver.ctx.root->probes.at(0)->block->stmts; // $x = (struct x*)ctx; auto assignment = static_cast(stmts.at(0)); EXPECT_TRUE(assignment->var->type.IsPtrTy()); // $a = $x->a; assignment = static_cast(stmts.at(1)); EXPECT_EQ(CreateInt64(), assignment->var->type); auto fieldaccess = static_cast(assignment->expr); EXPECT_EQ(CreateInt64(), fieldaccess->type); auto unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsCtxAccess()); auto var = static_cast(unop->expr); EXPECT_TRUE(var->type.IsPtrTy()); // $b = $x->b[0]; assignment = static_cast(stmts.at(2)); EXPECT_EQ(CreateInt16(), assignment->var->type); auto arrayaccess = static_cast(assignment->expr); EXPECT_EQ(CreateInt16(), arrayaccess->type); fieldaccess = static_cast(arrayaccess->expr); EXPECT_TRUE(fieldaccess->type.IsCtxAccess()); unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsCtxAccess()); var = static_cast(unop->expr); EXPECT_TRUE(var->type.IsPtrTy()); #ifdef __x86_64__ auto chartype = CreateInt8(); #else auto chartype = CreateUInt8(); #endif // $c = $x->c.c; assignment = static_cast(stmts.at(3)); EXPECT_EQ(chartype, assignment->var->type); fieldaccess = static_cast(assignment->expr); EXPECT_EQ(chartype, fieldaccess->type); fieldaccess = static_cast(fieldaccess->expr); EXPECT_TRUE(fieldaccess->type.IsCtxAccess()); unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsCtxAccess()); var = static_cast(unop->expr); EXPECT_TRUE(var->type.IsPtrTy()); // $d = $x->d->c; assignment = static_cast(stmts.at(4)); EXPECT_EQ(chartype, assignment->var->type); fieldaccess = static_cast(assignment->expr); EXPECT_EQ(chartype, fieldaccess->type); unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsRecordTy()); fieldaccess = static_cast(unop->expr); EXPECT_TRUE(fieldaccess->type.IsPtrTy()); unop = static_cast(fieldaccess->expr); EXPECT_TRUE(unop->type.IsCtxAccess()); var = static_cast(unop->expr); EXPECT_TRUE(var->type.IsPtrTy()); test(driver, "k:f, kr:f { @ = (uint64)ctx; }"); test(driver, "k:f, i:s:1 { @ = (uint64)ctx; }", 1); test(driver, "t:sched:sched_one { @ = (uint64)ctx; }", 1); } TEST(semantic_analyser, double_pointer_basic) { test(R"_(BEGIN { $pp = (int8 **)0; $p = *$pp; $val = *$p; })_"); test(R"_(BEGIN { $pp = (int8 **)0; $val = **$pp; })_"); const std::string structs = "struct Foo { int x; }"; test(structs + R"_(BEGIN { $pp = (struct Foo **)0; $val = (*$pp)->x; })_"); } TEST(semantic_analyser, double_pointer_int) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "kprobe:f { $pp = (int8 **)1; $p = *$pp; $val = *$p; }"); auto &stmts = driver.ctx.root->probes.at(0)->block->stmts; // $pp = (int8 **)1; auto assignment = static_cast(stmts.at(0)); ASSERT_TRUE(assignment->var->type.IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->GetPointeeTy()->IsIntTy()); EXPECT_EQ( assignment->var->type.GetPointeeTy()->GetPointeeTy()->GetIntBitWidth(), 8ULL); // $p = *$pp; assignment = static_cast(stmts.at(1)); ASSERT_TRUE(assignment->var->type.IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->IsIntTy()); EXPECT_EQ(assignment->var->type.GetPointeeTy()->GetIntBitWidth(), 8ULL); // $val = *$p; assignment = static_cast(stmts.at(2)); ASSERT_TRUE(assignment->var->type.IsIntTy()); EXPECT_EQ(assignment->var->type.GetIntBitWidth(), 8ULL); } TEST(semantic_analyser, double_pointer_struct) { BPFtrace bpftrace; Driver driver(bpftrace); test(driver, "struct Foo { char x; long y; }" "kprobe:f { $pp = (struct Foo **)1; $p = *$pp; $val = $p->x; }"); auto &stmts = driver.ctx.root->probes.at(0)->block->stmts; // $pp = (struct Foo **)1; auto assignment = static_cast(stmts.at(0)); ASSERT_TRUE(assignment->var->type.IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->IsPtrTy()); ASSERT_TRUE( assignment->var->type.GetPointeeTy()->GetPointeeTy()->IsRecordTy()); EXPECT_EQ(assignment->var->type.GetPointeeTy()->GetPointeeTy()->GetName(), "struct Foo"); // $p = *$pp; assignment = static_cast(stmts.at(1)); ASSERT_TRUE(assignment->var->type.IsPtrTy()); ASSERT_TRUE(assignment->var->type.GetPointeeTy()->IsRecordTy()); EXPECT_EQ(assignment->var->type.GetPointeeTy()->GetName(), "struct Foo"); // $val = $p->x; assignment = static_cast(stmts.at(2)); ASSERT_TRUE(assignment->var->type.IsIntTy()); EXPECT_EQ(assignment->var->type.GetIntBitWidth(), 8ULL); } TEST(semantic_analyser, pointer_arith) { test(R"_(BEGIN { $t = (int32*) 32; $t = $t + 1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $t +=1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $t++ })_"); test(R"_(BEGIN { $t = (int32*) 32; ++$t })_"); test(R"_(BEGIN { $t = (int32*) 32; $t = $t - 1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $t -=1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $t-- })_"); test(R"_(BEGIN { $t = (int32*) 32; --$t })_"); // pointer compare test(R"_(BEGIN { $t = (int32*) 32; @ = ($t > $t); })_"); test(R"_(BEGIN { $t = (int32*) 32; @ = ($t < $t); })_"); test(R"_(BEGIN { $t = (int32*) 32; @ = ($t >= $t); })_"); test(R"_(BEGIN { $t = (int32*) 32; @ = ($t <= $t); })_"); test(R"_(BEGIN { $t = (int32*) 32; @ = ($t == $t); })_"); // map test(R"_(BEGIN { @ = (int32*) 32; @ = @ + 1 })_"); test(R"_(BEGIN { @ = (int32*) 32; @ +=1 })_"); test(R"_(BEGIN { @ = (int32*) 32; @++ })_"); test(R"_(BEGIN { @ = (int32*) 32; ++@ })_"); test(R"_(BEGIN { @ = (int32*) 32; @ = @ - 1 })_"); test(R"_(BEGIN { @ = (int32*) 32; @ -=1 })_"); test(R"_(BEGIN { @ = (int32*) 32; @-- })_"); test(R"_(BEGIN { @ = (int32*) 32; --@ })_"); // associativity test(R"_(BEGIN { $t = (int32*) 32; $t = $t + 1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $t = 1 + $t })_"); test(R"_(BEGIN { $t = (int32*) 32; $t = $t - 1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $t = 1 - $t })_", 1); // invalid ops test(R"_(BEGIN { $t = (int32*) 32; $t *= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t /= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t %= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t <<= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t >>= 5 })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t -= $t })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t += $t })_", 1); // invalid types test(R"_(BEGIN { $t = (int32*) 32; $t += "abc" })_", 1); test(R"_(BEGIN { $t = (int32*) 32; $t += comm })_", 1); test( R"_(struct A {}; BEGIN { $t = (int32*) 32; $s = *(struct A*) 0; $t += $s })_", 1); } TEST(semantic_analyser, pointer_compare) { test(R"_(BEGIN { $t = (int32*) 32; $c = $t < 1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $c = $t > 1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $c = $t <= 1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $c = $t >= 1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $c = $t != 1 })_"); test(R"_(BEGIN { $t = (int32*) 32; $c = $t < $t })_"); test(R"_(BEGIN { $t = (int32*) 32; $c = $t > $t })_"); test(R"_(BEGIN { $t = (int32*) 32; $c = $t <= $t })_"); test(R"_(BEGIN { $t = (int32*) 32; $c = $t >= $t })_"); test(R"_(BEGIN { $t = (int32*) 32; $c = $t != $t })_"); // pointer compare diff types test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t > $y); })_"); test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t < $y); })_"); test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t >= $y); })_"); test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t <= $y); })_"); test(R"_(BEGIN { $t = (int32*) 32; $y = (int64*) 1024; @ = ($t == $y); })_"); test_for_warning("k:f { $a = (int8*) 1; $b = (int16*) 2; $c = ($a == $b) }", "comparison of distinct pointer types ('int8, 'int16')"); } // Basic functionality test TEST(semantic_analyser, tuple) { test(R"_(BEGIN { $t = (1)})_"); test(R"_(BEGIN { $t = (1, 2); $v = $t;})_"); test(R"_(BEGIN { $t = (1, 2, "string")})_"); test(R"_(BEGIN { $t = (1, 2, "string"); $t = (3, 4, "other"); })_"); test(R"_(BEGIN { $t = (1, kstack()) })_"); test(R"_(BEGIN { $t = (1, (2,3)) })_"); test(R"_(BEGIN { @t = (1)})_"); test(R"_(BEGIN { @t = (1, 2); @v = @t;})_"); test(R"_(BEGIN { @t = (1, 2, "string")})_"); test(R"_(BEGIN { @t = (1, 2, "string"); @t = (3, 4, "other"); })_"); test(R"_(BEGIN { @t = (1, kstack()) })_"); test(R"_(BEGIN { @t = (1, (2,3)) })_"); test(R"_(BEGIN { $t = (1, (int64)2); $t = (2, (int32)3); })_"); test_error(R"_(BEGIN { $t = (1, (int32)2); $t = (2, (int64)3); })_", R"( stdin:1:29-47: ERROR: Type mismatch for $t: trying to assign value of type '(int64,int64)' when variable already contains a value of type '(int64,int32)' BEGIN { $t = (1, (int32)2); $t = (2, (int64)3); } ~~~~~~~~~~~~~~~~~~ )"); test(R"_(struct task_struct { int x; } BEGIN { $t = (1, curtask); })_"); test(R"_(struct task_struct { int x[4]; } BEGIN { $t = (1, curtask->x); })_"); test(R"_(BEGIN { $t = (1, 2); $t = (4, "other"); })_", 1); test(R"_(BEGIN { $t = (1, 2); $t = 5; })_", 1); test(R"_(BEGIN { $t = (1, count()) })_", 1); test(R"_(BEGIN { @t = (1, 2); @t = (4, "other"); })_", 3); test(R"_(BEGIN { @t = (1, 2); @t = 5; })_", 1); test(R"_(BEGIN { @t = (1, count()) })_", 1); test(R"_(BEGIN { $t = (1, (2, 3)); $t = (4, ((int8)5, 6)); })_"); test_error(R"_(BEGIN { $t = (1, ((int8)2, 3)); $t = (4, (5, 6)); })_", R"( stdin:1:33-49: ERROR: Type mismatch for $t: trying to assign value of type '(int64,(int64,int64))' when variable already contains a value of type '(int64,(int8,int64))' BEGIN { $t = (1, ((int8)2, 3)); $t = (4, (5, 6)); } ~~~~~~~~~~~~~~~~ )"); test_error(R"_(BEGIN { $t = ((uint8)1, (2, 3)); $t = (4, ((int8)5, 6)); })_", R"( stdin:1:34-56: ERROR: Type mismatch for $t: trying to assign value of type '(int64,(int8,int64))' when variable already contains a value of type '(uint8,(int64,int64))' BEGIN { $t = ((uint8)1, (2, 3)); $t = (4, ((int8)5, 6)); } ~~~~~~~~~~~~~~~~~~~~~~ )"); test(R"_(BEGIN { @t = (1, 2, "hi"); @t = (3, 4, "hellolongstr"); })_"); test(R"_(BEGIN { $t = (1, ("hi", 2)); $t = (3, ("hellolongstr", 4)); })_"); test_error("BEGIN { @x[1] = hist(10); $y = (1, @x[1]); }", R"( stdin:1:36-41: ERROR: Map type hist_t cannot exist inside a tuple. BEGIN { @x[1] = hist(10); $y = (1, @x[1]); } ~~~~~ )"); } TEST(semantic_analyser, tuple_indexing) { test(R"_(BEGIN { (1,2).0 })_"); test(R"_(BEGIN { (1,2).1 })_"); test(R"_(BEGIN { (1,2,3).2 })_"); test(R"_(BEGIN { $t = (1,2,3).0 })_"); test(R"_(BEGIN { $t = (1,2,3); $v = $t.0; })_"); test(R"_(BEGIN { (1,2,3).3 })_", 2); test(R"_(BEGIN { (1,2,3).9999999999999 })_", 2); } // More in depth inspection of AST TEST(semantic_analyser, tuple_assign_var) { BPFtrace bpftrace; Driver driver(bpftrace); SizedType ty = CreateTuple( bpftrace.structs.AddTuple({ CreateInt64(), CreateString(6) })); test(bpftrace, true, driver, R"_(BEGIN { $t = (1, "str"); $t = (4, "other"); })_", 0); auto &stmts = driver.ctx.root->probes.at(0)->block->stmts; // $t = (1, "str"); auto assignment = static_cast(stmts.at(0)); EXPECT_EQ(ty, assignment->var->type); // $t = (4, "other"); assignment = static_cast(stmts.at(1)); EXPECT_EQ(ty, assignment->var->type); } // More in depth inspection of AST TEST(semantic_analyser, tuple_assign_map) { BPFtrace bpftrace; Driver driver(bpftrace); SizedType ty; test(bpftrace, true, driver, R"_(BEGIN { @ = (1, 3, 3, 7); @ = (0, 0, 0, 0); })_", 0); auto &stmts = driver.ctx.root->probes.at(0)->block->stmts; // $t = (1, 3, 3, 7); auto assignment = static_cast(stmts.at(0)); ty = CreateTuple(bpftrace.structs.AddTuple( { CreateInt64(), CreateInt64(), CreateInt64(), CreateInt64() })); EXPECT_EQ(ty, assignment->map->type); // $t = (0, 0, 0, 0); assignment = static_cast(stmts.at(1)); ty = CreateTuple(bpftrace.structs.AddTuple( { CreateInt64(), CreateInt64(), CreateInt64(), CreateInt64() })); EXPECT_EQ(ty, assignment->map->type); } // More in depth inspection of AST TEST(semantic_analyser, tuple_nested) { BPFtrace bpftrace; Driver driver(bpftrace); SizedType ty_inner = CreateTuple( bpftrace.structs.AddTuple({ CreateInt64(), CreateInt64() })); SizedType ty = CreateTuple( bpftrace.structs.AddTuple({ CreateInt64(), ty_inner })); test(bpftrace, true, driver, R"_(BEGIN { $t = (1,(1,2)); })_", 0); auto &stmts = driver.ctx.root->probes.at(0)->block->stmts; // $t = (1, "str"); auto assignment = static_cast(stmts.at(0)); EXPECT_EQ(ty, assignment->var->type); } TEST(semantic_analyser, tuple_types_unique) { auto bpftrace = get_mock_bpftrace(); test(*bpftrace, R"_(BEGIN { $t = (1, "hello"); $t = (4, "other"); })_"); EXPECT_EQ(bpftrace->structs.GetTuplesCnt(), 1ul); } TEST(semantic_analyser, multi_pass_type_inference_zero_size_int) { auto bpftrace = get_mock_bpftrace(); // The first pass on processing the Unop does not have enough information // to figure out size of `@i` yet. The analyzer figures out the size // after seeing the `@i++`. On the second pass the correct size is // determined. test(*bpftrace, "BEGIN { if (!@i) { @i++; } }"); } TEST(semantic_analyser, call_kptr_uptr) { test("k:f { @ = kptr((int8*) arg0); }"); test("k:f { $a = kptr((int8*) arg0); }"); test("k:f { @ = kptr(arg0); }"); test("k:f { $a = kptr(arg0); }"); test("k:f { @ = uptr((int8*) arg0); }"); test("k:f { $a = uptr((int8*) arg0); }"); test("k:f { @ = uptr(arg0); }"); test("k:f { $a = uptr(arg0); }"); } TEST(semantic_analyser, call_path) { test("kprobe:f { $k = path( arg0 ) }", 1); test("kretprobe:f { $k = path( arg0 ) }", 1); test("tracepoint:category:event { $k = path( NULL ) }", 1); test("kprobe:f { $k = path( arg0 ) }", 1); test("kretprobe:f{ $k = path( \"abc\" ) }", 1); test("tracepoint:category:event { $k = path( -100 ) }", 1); test("uprobe:/bin/sh:f { $k = path( arg0 ) }", 1); test("BEGIN { $k = path( 1 ) }", 1); test("END { $k = path( 1 ) }", 1); } TEST(semantic_analyser, call_offsetof) { test("struct Foo { int x; long l; char c; } \ BEGIN { @x = offsetof(struct Foo, x); }"); test("struct Foo { int comm; } \ BEGIN { @x = offsetof(struct Foo, comm); }"); test("struct Foo { int ctx; } \ BEGIN { @x = offsetof(struct Foo, ctx); }"); test("struct Foo { int args; } \ BEGIN { @x = offsetof(struct Foo, args); }"); test("struct Foo { int x; long l; char c; } \ struct Bar { struct Foo foo; int x; } \ BEGIN { @x = offsetof(struct Bar, x); }"); test("struct Foo { int x; long l; char c; } \ union Bar { struct Foo foo; int x; } \ BEGIN { @x = offsetof(union Bar, x); }"); test("struct Foo { int x; long l; char c; } \ struct Fun { struct Foo foo; int (*call)(void); } \ BEGIN { @x = offsetof(struct Fun, call); }"); test("struct Foo { int x; long l; char c; } \ BEGIN { $foo = (struct Foo *)0; \ @x = offsetof(*$foo, x); }"); test("struct Foo { int x; long l; char c; } \ struct Ano { \ struct { \ struct Foo foo; \ int a; \ }; \ long l; \ } \ BEGIN { @x = offsetof(struct Ano, a); }"); test("struct Foo { struct Bar { int a; } bar; } \ BEGIN { @x = offsetof(struct Foo, bar.a); }"); test("struct Foo { struct Bar { int *a; } bar; } \ BEGIN { @x = offsetof(struct Foo, bar.a); }"); test("struct Foo { struct Bar { struct { int a; } anon; } bar; } \ BEGIN { @x = offsetof(struct Foo, bar.anon.a); }"); test("struct Foo { struct Bar { struct { int a; }; } bar; } \ BEGIN { @x = offsetof(struct Foo, bar.a); }"); // Error tests // Bad type test_error("struct Foo { struct Bar { int a; } *bar; } \ BEGIN { @x = offsetof(struct Foo, bar.a); }", R"( stdin:1:71-99: ERROR: 'struct Bar *' is not a record type. struct Foo { struct Bar { int a; } *bar; } BEGIN { @x = offsetof(struct Foo, bar.a); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); // Not exist (sub)field test_error("struct Foo { int x; long l; char c; } \ BEGIN { @x = offsetof(struct Foo, __notexistfield__); }", R"( stdin:1:66-106: ERROR: 'struct Foo' has no field named '__notexistfield__' struct Foo { int x; long l; char c; } BEGIN { @x = offsetof(struct Foo, __notexistfield__); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); test_error("struct Foo { struct Bar { int a; } bar; } \ BEGIN { @x = offsetof(struct Foo, bar.__notexist_subfield__); }", R"( stdin:1:70-118: ERROR: 'struct Bar' has no field named '__notexist_subfield__' struct Foo { struct Bar { int a; } bar; } BEGIN { @x = offsetof(struct Foo, bar.__notexist_subfield__); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); // Not exist record test("BEGIN { @x = offsetof(__passident__, x); }", 1); test("BEGIN { @x = offsetof(__passident__, x.y.z); }", 1); test("BEGIN { @x = offsetof(struct __notexiststruct__, x); }", 1); test("BEGIN { @x = offsetof(struct __notexiststruct__, x.y.z); }", 1); } TEST(semantic_analyser, int_ident) { test("BEGIN { sizeof(int32) }"); } TEST(semantic_analyser, tracepoint_common_field) { test("tracepoint:file:filename { args.filename }"); 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.ctx.root->probes.at(0)->block->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.ctx.root->probes.at(0)->block->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.ctx.root->probes.at(0)->block->stmts.at(0); map_assign = dynamic_cast(stmt); ASSERT_TRUE(map_assign->map->key_expr->type.IsStringTy()); ASSERT_EQ(map_assign->map->key_expr->type.GetSize(), 3UL); ASSERT_EQ(map_assign->map->key_type.GetSize(), 6UL); test(bpftrace, true, driver, R"_(k:f1 {@["hi", 0] = 0;} k:f2 {@["hello", 1] = 1;})_", 0); stmt = driver.ctx.root->probes.at(0)->block->stmts.at(0); map_assign = dynamic_cast(stmt); ASSERT_TRUE(map_assign->map->key_expr->type.IsTupleTy()); ASSERT_TRUE(map_assign->map->key_expr->type.GetField(0).type.IsStringTy()); ASSERT_EQ(map_assign->map->key_expr->type.GetField(0).type.GetSize(), 3UL); ASSERT_EQ(map_assign->map->key_type.GetField(0).type.GetSize(), 6UL); ASSERT_EQ(map_assign->map->key_expr->type.GetSize(), 16UL); ASSERT_EQ(map_assign->map->key_type.GetSize(), 16UL); test(bpftrace, true, driver, R"_(k:f1 {$x = ("hello", 0);} k:f2 {$x = ("hi", 0); })_", 0); stmt = driver.ctx.root->probes.at(0)->block->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); } TEST(semantic_analyser, call_nsecs) { test("BEGIN { $ns = nsecs(); }"); test("BEGIN { $ns = nsecs(monotonic); }"); test("BEGIN { $ns = nsecs(boot); }"); MockBPFfeature hasfeature(true); test(hasfeature, "BEGIN { $ns = nsecs(tai); }"); test("BEGIN { $ns = nsecs(sw_tai); }"); test_error("BEGIN { $ns = nsecs(xxx); }", R"( stdin:1:15-24: ERROR: Invalid timestamp mode: xxx BEGIN { $ns = nsecs(xxx); } ~~~~~~~~~ )"); } TEST(semantic_analyser, config) { test("config = { BPFTRACE_MAX_AST_NODES=1 } BEGIN { $ns = nsecs(); }"); test("config = { BPFTRACE_MAX_AST_NODES=1; stack_mode=raw } BEGIN { $ns = " "nsecs(); }"); } TEST(semantic_analyser, subprog_return) { test("fn f(): void { return; }"); test("fn f(): int64 { return 1; }"); // Error location is incorrect: #3063 test_error("fn f(): void { return 1; }", R"( stdin:1:17-25: ERROR: Function f is of type void, cannot return int64 fn f(): void { return 1; } ~~~~~~~~ )"); // Error location is incorrect: #3063 test_error("fn f(): int64 { return; }", R"( stdin:1:18-24: ERROR: Function f is of type int64, cannot return void fn f(): int64 { return; } ~~~~~~ )"); } TEST(semantic_analyser, subprog_arguments) { test("fn f($a : int64): int64 { return $a; }"); // Error location is incorrect: #3063 test_error("fn f($a : int64): string[16] { return $a; }", R"( stdin:1:34-43: ERROR: Function f is of type string[16], cannot return int64 fn f($a : int64): string[16] { return $a; } ~~~~~~~~~ )"); } TEST(semantic_analyser, subprog_map) { test("fn f(): void { @a = 0; }"); test("fn f(): int64 { @a = 0; return @a + 1; }"); test("fn f(): void { @a[0] = 0; }"); test("fn f(): int64 { @a[0] = 0; return @a[0] + 1; }"); } TEST(semantic_analyser, subprog_builtin) { test("fn f(): void { print(\"Hello world\"); }"); test("fn f(): uint64 { return sizeof(int64); }"); test("fn f(): uint64 { return nsecs; }"); } TEST(semantic_analyser, subprog_buildin_disallowed) { // Error location is incorrect: #3063 test_error("fn f(): int64 { return func; }", R"( stdin:1:25-29: ERROR: Builtin func not supported outside probe fn f(): int64 { return func; } ~~~~ stdin:1:18-29: ERROR: Function f is of type int64, cannot return none fn f(): int64 { return func; } ~~~~~~~~~~~ )"); } class semantic_analyser_btf : public test_btf {}; TEST_F(semantic_analyser_btf, fentry) { test("fentry:func_1 { 1 }"); test("fexit:func_1 { 1 }"); test("fentry:func_1 { $x = args.a; $y = args.foo1; $z = args.foo2->f.a; }"); test("fexit:func_1 { $x = retval; }"); test("fentry:vmlinux:func_1 { 1 }"); test("fentry:*:func_1 { 1 }"); test_error("fexit:func_1 { $x = args.foo; }", R"( stdin:1:21-26: ERROR: Can't find function parameter foo fexit:func_1 { $x = args.foo; } ~~~~~ )"); test("fexit:func_1 { $x = args; }"); test("fentry:func_1 { @ = args; }"); test("fentry:func_1 { @[args] = 1; }"); // reg() is not available in fentry #ifdef __x86_64__ test_error("fentry:func_1 { reg(\"ip\") }", R"( stdin:1:17-26: ERROR: reg can not be used with "fentry" probes fentry:func_1 { reg("ip") } ~~~~~~~~~ )"); test_error("fexit:func_1 { reg(\"ip\") }", R"( stdin:1:16-25: ERROR: reg can not be used with "fexit" probes fexit:func_1 { reg("ip") } ~~~~~~~~~ )"); #endif // Backwards compatibility test("fentry:func_1 { $x = args->a; }"); } TEST_F(semantic_analyser_btf, short_name) { test("f:func_1 { 1 }"); test("fr:func_1 { 1 }"); } TEST_F(semantic_analyser_btf, call_path) { test("fentry:func_1 { @k = path( args.foo1 ) }"); test("fexit:func_1 { @k = path( retval->foo1 ) }"); test("fentry:func_1 { path( args.foo1, 16);}"); test("fentry:func_1 { path( args.foo1, \"Na\");}", 1); test("fentry:func_1 { path( args.foo1, -1);}", 1); } TEST_F(semantic_analyser_btf, call_skb_output) { test("fentry:func_1 { $ret = skboutput(\"one.pcap\", args.foo1, 1500, 0); }"); test("fexit:func_1 { $ret = skboutput(\"one.pcap\", args.foo1, 1500, 0); " "}"); test_error("fentry:func_1 { $ret = skboutput(); }", R"( stdin:1:24-35: ERROR: skboutput() requires 4 arguments (0 provided) fentry:func_1 { $ret = skboutput(); } ~~~~~~~~~~~ )"); test_error("fentry:func_1 { $ret = skboutput(\"one.pcap\"); }", R"( stdin:1:24-45: ERROR: skboutput() requires 4 arguments (1 provided) fentry:func_1 { $ret = skboutput("one.pcap"); } ~~~~~~~~~~~~~~~~~~~~~ )"); test_error("fentry:func_1 { $ret = skboutput(\"one.pcap\", args.foo1); }", R"( stdin:1:24-56: ERROR: skboutput() requires 4 arguments (2 provided) fentry:func_1 { $ret = skboutput("one.pcap", args.foo1); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); test_error( "fentry:func_1 { $ret = skboutput(\"one.pcap\", args.foo1, 1500); }", R"( stdin:1:24-62: ERROR: skboutput() requires 4 arguments (3 provided) fentry:func_1 { $ret = skboutput("one.pcap", args.foo1, 1500); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); test_error("fentry:func_1 { skboutput(\"one.pcap\", args.foo1, 1500, 0); }", R"( stdin:1:17-58: ERROR: skboutput() should be assigned to a variable fentry:func_1 { skboutput("one.pcap", args.foo1, 1500, 0); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )"); } TEST_F(semantic_analyser_btf, call_percpu_kaddr) { test("kprobe:f { percpu_kaddr(\"process_counts\"); }"); test("kprobe:f { percpu_kaddr(\"process_counts\", 0); }"); test("kprobe:f { @x = percpu_kaddr(\"process_counts\"); }"); test("kprobe:f { @x = percpu_kaddr(\"process_counts\", 0); }"); test("kprobe:f { percpu_kaddr(); }", 1); test("kprobe:f { percpu_kaddr(0); }", 1); test_error("kprobe:f { percpu_kaddr(\"nonsense\"); }", R"( stdin:1:12-36: ERROR: Could not resolve variable "nonsense" from BTF kprobe:f { percpu_kaddr("nonsense"); } ~~~~~~~~~~~~~~~~~~~~~~~~ )", false); } TEST_F(semantic_analyser_btf, iter) { test("iter:task { 1 }"); test("iter:task { $x = ctx->task->pid }"); test("iter:task_file { $x = ctx->file->ino }"); test("iter:task_vma { $x = ctx->vma->vm_start }"); test("iter:task { printf(\"%d\", ctx->task->pid); }"); test_error("iter:task { $x = args.foo; }", R"( stdin:1:18-22: ERROR: The args builtin can only be used with tracepoint/fentry/uprobe probes (iter used here) iter:task { $x = args.foo; } ~~~~ )"); test_error("iter:task,iter:task_file { 1 }", R"( stdin:1:1-10: ERROR: Only single iter attach point is allowed. iter:task,iter:task_file { 1 } ~~~~~~~~~ )"); test_error("iter:task,f:func_1 { 1 }", R"( stdin:1:1-10: ERROR: Only single iter attach point is allowed. iter:task,f:func_1 { 1 } ~~~~~~~~~ )"); } // Sanity check for kfunc/kretfunc aliases TEST_F(semantic_analyser_btf, kfunc) { test("kfunc:func_1 { 1 }"); test("kretfunc:func_1 { 1 }"); test("kfunc:func_1 { $x = args.a; $y = args.foo1; $z = args.foo2->f.a; }"); test("kretfunc:func_1 { $x = retval; }"); test("kfunc:vmlinux:func_1 { 1 }"); test("kfunc:*:func_1 { 1 }"); test("kfunc:func_1 { @[func] = 1; }"); test_error("kretfunc:func_1 { $x = args.foo; }", R"( stdin:1:24-29: ERROR: Can't find function parameter foo kretfunc:func_1 { $x = args.foo; } ~~~~~ )"); test("kretfunc:func_1 { $x = args; }"); test("kfunc:func_1 { @ = args; }"); test("kfunc:func_1 { @[args] = 1; }"); // reg() is not available in kfunc #ifdef __x86_64__ test_error("kfunc:func_1 { reg(\"ip\") }", R"( stdin:1:16-25: ERROR: reg can not be used with "fentry" probes kfunc:func_1 { reg("ip") } ~~~~~~~~~ )"); test_error("kretfunc:func_1 { reg(\"ip\") }", R"( stdin:1:19-28: ERROR: reg can not be used with "fexit" probes kretfunc:func_1 { reg("ip") } ~~~~~~~~~ )"); #endif // Backwards compatibility test("kfunc:func_1 { $x = args->a; }"); } TEST(semantic_analyser, btf_type_tags) { test("t:btf:tag { args.parent }"); test_error("t:btf:tag { args.real_parent }", R"( stdin:1:13-18: ERROR: Attempting to access pointer field 'real_parent' with unsupported tag attribute: percpu t:btf:tag { args.real_parent } ~~~~~ )"); } TEST(semantic_analyser, for_loop_map_one_key) { test("BEGIN { @map[0] = 1; for ($kv : @map) { print($kv); } }", R"( Program BEGIN = map: @map :: [int64] int: 0 :: [int64] int: 1 :: [int64] for decl variable: $kv :: [(int64,int64)] expr map: @map :: [int64] stmts call: print variable: $kv :: [(int64,int64)] )"); } TEST(semantic_analyser, for_loop_map_two_keys) { test("BEGIN { @map[0,0] = 1; for ($kv : @map) { print($kv); } }", R"( Program BEGIN = map: @map :: [int64] tuple: :: [(int64,int64)] int: 0 :: [int64] int: 0 :: [int64] int: 1 :: [int64] for decl variable: $kv :: [((int64,int64),int64)] expr map: @map :: [int64] stmts call: print variable: $kv :: [((int64,int64),int64)] )"); } TEST(semantic_analyser, for_loop_map) { test("BEGIN { @map[0] = 1; for ($kv : @map) { print($kv); } }"); test("BEGIN { @map[0] = 1; for ($kv : @map) { print($kv.0); } }"); test("BEGIN { @map[0] = 1; for ($kv : @map) { print($kv.1); } }"); test("BEGIN { @map1[@map2] = 1; @map2 = 1; for ($kv : @map1) { print($kv); } " "}"); } TEST(semantic_analyser, for_loop_map_declared_after) { // Regression test: What happens with @map[$kv.0] when @map hasn't been // defined yet? test("BEGIN { for ($kv : @map) { @map[$kv.0] } @map[0] = 1; }"); } TEST(semantic_analyser, for_loop_map_no_key) { // Error location is incorrect: #3063 test_error("BEGIN { @map = 1; for ($kv : @map) { } }", R"( stdin:1:30-35: ERROR: Maps used as for-loop expressions must have keys to iterate over BEGIN { @map = 1; for ($kv : @map) { } } ~~~~~ )"); } TEST(semantic_analyser, for_loop_map_undefined) { // Error location is incorrect: #3063 test_error("BEGIN { for ($kv : @map) { } }", R"( stdin:1:20-25: ERROR: Undefined map: @map BEGIN { for ($kv : @map) { } } ~~~~~ )"); } TEST(semantic_analyser, for_loop_map_undefined2) { // Error location is incorrect: #3063 test_error("BEGIN { @map[0] = 1; for ($kv : @undef) { @map[$kv.0]; } }", R"( stdin:1:33-40: ERROR: Undefined map: @undef BEGIN { @map[0] = 1; for ($kv : @undef) { @map[$kv.0]; } } ~~~~~~~ )"); } TEST(semantic_analyser, for_loop_map_restricted_types) { test_error("BEGIN { @map[0] = hist(10); for ($kv : @map) { } }", R"( stdin:1:40-45: ERROR: Loop expression does not support type: hist_t BEGIN { @map[0] = hist(10); for ($kv : @map) { } } ~~~~~ )"); test_error("BEGIN { @map[0] = lhist(10, 0, 10, 1); for ($kv : @map) { } }", R"( stdin:1:51-56: ERROR: Loop expression does not support type: lhist_t BEGIN { @map[0] = lhist(10, 0, 10, 1); for ($kv : @map) { } } ~~~~~ )"); test_error("BEGIN { @map[0] = stats(10); for ($kv : @map) { } }", R"( stdin:1:41-46: ERROR: Loop expression does not support type: stats_t BEGIN { @map[0] = stats(10); for ($kv : @map) { } } ~~~~~ )"); } TEST(semantic_analyser, for_loop_shadowed_decl) { test_error(R"( BEGIN { $kv = 1; @map[0] = 1; for ($kv : @map) { } })", R"( stdin:4:11-15: ERROR: Loop declaration shadows existing variable: $kv for ($kv : @map) { } ~~~~ )"); } TEST(semantic_analyser, for_loop_variables_read_only) { test(R"( BEGIN { $var = 0; @map[0] = 1; for ($kv : @map) { print($var); } print($var); })", R"(* for ctx $var :: [int64 *, AS(bpf)] decl *)"); } TEST(semantic_analyser, for_loop_variables_modified_during_loop) { test(R"( BEGIN { $var = 0; @map[0] = 1; for ($kv : @map) { $var++; } print($var); })", R"(* for ctx $var :: [int64 *, AS(bpf)] decl *)"); } TEST(semantic_analyser, for_loop_variables_created_in_loop) { // $var should not appear in ctx test(R"( BEGIN { @map[0] = 1; for ($kv : @map) { $var = 2; print($var); } })", R"(* for decl *)"); } TEST(semantic_analyser, for_loop_variables_multiple) { test(R"( BEGIN { @map[0] = 1; $var1 = 123; $var2 = "abc"; $var3 = "def"; for ($kv : @map) { $var1 = 456; print($var3); } })", R"(* for ctx $var1 :: [int64 *, AS(bpf)] $var3 :: [string[4] *, AS(bpf)] decl *)"); } TEST(semantic_analyser, for_loop_variables_created_in_loop_used_after) { test_error(R"( BEGIN { @map[0] = 1; for ($kv : @map) { $var = 2; } print($var); })", R"( stdin:6:7-17: ERROR: Undefined or undeclared variable: $var print($var); ~~~~~~~~~~ )"); test_error(R"( BEGIN { @map[0] = 1; for ($kv : @map) { print($kv); } print($kv); })", R"( stdin:6:7-16: ERROR: Undefined or undeclared variable: $kv print($kv); ~~~~~~~~~ )"); } TEST(semantic_analyser, for_loop_invalid_expr) { // Error location is incorrect: #3063 test_error("BEGIN { for ($x : $var) { } }", R"( stdin:1:19-24: ERROR: Loop expression must be a map BEGIN { for ($x : $var) { } } ~~~~~ )"); test_error("BEGIN { for ($x : 1+2) { } }", R"( stdin:1:19-22: ERROR: Loop expression must be a map BEGIN { for ($x : 1+2) { } } ~~~ )"); test_error("BEGIN { for ($x : \"abc\") { } }", R"( stdin:1:19-25: ERROR: Loop expression must be a map BEGIN { for ($x : "abc") { } } ~~~~~~ )"); } TEST(semantic_analyser, for_loop_multiple_errors) { // Error location is incorrect: #3063 test_error(R"( BEGIN { $kv = 1; @map[0] = 1; for ($kv : 1) { } })", R"( stdin:4:11-15: ERROR: Loop declaration shadows existing variable: $kv for ($kv : 1) { } ~~~~ stdin:4:18-20: ERROR: Loop expression must be a map for ($kv : 1) { } ~~ )"); } TEST(semantic_analyser, for_loop_control_flow) { // Error location is incorrect: #3063 test_error("BEGIN { @map[0] = 1; for ($kv : @map) { break; } }", R"( stdin:1:42-47: ERROR: 'break' statement is not allowed in a for-loop BEGIN { @map[0] = 1; for ($kv : @map) { break; } } ~~~~~ )"); // Error location is incorrect: #3063 test_error("BEGIN { @map[0] = 1; for ($kv : @map) { continue; } }", R"( stdin:1:42-50: ERROR: 'continue' statement is not allowed in a for-loop BEGIN { @map[0] = 1; for ($kv : @map) { continue; } } ~~~~~~~~ )"); // Error location is incorrect: #3063 test_error("BEGIN { @map[0] = 1; for ($kv : @map) { return; } }", R"( stdin:1:42-48: ERROR: 'return' statement is not allowed in a for-loop BEGIN { @map[0] = 1; for ($kv : @map) { return; } } ~~~~~~ )"); } TEST(semantic_analyser, for_loop_missing_feature) { test_error("BEGIN { @map[0] = 1; for ($kv : @map) { print($kv); } }", R"( stdin:1:22-25: ERROR: Missing required kernel feature: for_each_map_elem BEGIN { @map[0] = 1; for ($kv : @map) { print($kv); } } ~~~ )", false); } TEST(semantic_analyser, for_loop_castable_map_missing_feature) { test_error("BEGIN { @map = count(); for ($kv : @map) { print($kv); } }", R"( stdin:1:25-28: ERROR: Missing required kernel feature: for_each_map_elem BEGIN { @map = count(); for ($kv : @map) { print($kv); } } ~~~ stdin:1:36-41: ERROR: Missing required kernel feature: map_lookup_percpu_elem BEGIN { @map = count(); for ($kv : @map) { print($kv); } } ~~~~~ )", false); } TEST(semantic_analyser, castable_map_missing_feature) { MockBPFfeature feature(false); test(feature, "k:f { @a = count(); }"); test(feature, "k:f { @a = count(); print(@a) }"); test(feature, "k:f { @a = count(); len(@a) }"); test(feature, "k:f { @a = count(); clear(@a) }"); test(feature, "k:f { @a = count(); zero(@a) }"); test(feature, "k:f { @a[1] = count(); delete(@a, 1) }"); test(feature, "k:f { @a[1] = count(); has_key(@a, 1) }"); test_error("BEGIN { @a = count(); print((uint64)@a) }", R"( stdin:1:23-39: ERROR: Missing required kernel feature: map_lookup_percpu_elem BEGIN { @a = count(); print((uint64)@a) } ~~~~~~~~~~~~~~~~ )", false); test_error("BEGIN { @a = count(); print((@a, 1)) }", R"( stdin:1:23-32: ERROR: Missing required kernel feature: map_lookup_percpu_elem BEGIN { @a = count(); print((@a, 1)) } ~~~~~~~~~ )", false); test_error("BEGIN { @a[1] = count(); print(@a[1]) }", R"( stdin:1:26-37: ERROR: Missing required kernel feature: map_lookup_percpu_elem BEGIN { @a[1] = count(); print(@a[1]) } ~~~~~~~~~~~ )", false); test_error("BEGIN { @a = count(); $b = @a; }", R"( stdin:1:28-30: ERROR: Missing required kernel feature: map_lookup_percpu_elem BEGIN { @a = count(); $b = @a; } ~~ )", false); test_error("BEGIN { @a = count(); @b = 1; @b = @a; }", R"( stdin:1:36-38: ERROR: Missing required kernel feature: map_lookup_percpu_elem BEGIN { @a = count(); @b = 1; @b = @a; } ~~ )", false); } TEST(semantic_analyser, for_loop_no_ctx_access) { test_error("kprobe:f { @map[0] = 1; for ($kv : @map) { arg0 } }", R"( stdin:1:45-49: ERROR: 'arg0' builtin is not allowed in a for-loop kprobe:f { @map[0] = 1; for ($kv : @map) { arg0 } } ~~~~ )"); } TEST_F(semantic_analyser_btf, args_builtin_mixed_probes) { test_error("fentry:func_1,tracepoint:sched:sched_one { args }", R"( stdin:1:44-48: ERROR: The args builtin can only be used within the context of a single probe type, e.g. "probe1 {args}" is valid while "probe1,probe2 {args}" is not. fentry:func_1,tracepoint:sched:sched_one { args } ~~~~ )"); } TEST_F(semantic_analyser_btf, binop_late_ptr_resolution) { test(R"(fentry:func_1 { if (@a[1] == args.foo1) { } @a[1] = args.foo1; })"); } TEST(semantic_analyser, buf_strlen_too_large) { auto bpftrace = get_mock_bpftrace(); ConfigSetter configs{ bpftrace->config_, ConfigSource::script }; configs.set(ConfigKeyInt::max_strlen, 9999999999); test_error(*bpftrace, "uprobe:/bin/sh:f { buf(arg0, 4) }", R"( stdin:1:20-32: ERROR: BPFTRACE_MAX_STRLEN too large to use on buffer (9999999999 > 4294967295) uprobe:/bin/sh:f { buf(arg0, 4) } ~~~~~~~~~~~~ )"); test_error(*bpftrace, "uprobe:/bin/sh:f { buf(arg0) }", R"( stdin:1:20-29: ERROR: BPFTRACE_MAX_STRLEN too large to use on buffer (9999999999 > 4294967295) uprobe:/bin/sh:f { buf(arg0) } ~~~~~~~~~ )"); } TEST(semantic_analyser, variable_declarations) { test("BEGIN { let $a; $a = 1; }"); test("BEGIN { let $a: int16; $a = 1; }"); test("BEGIN { let $a = 1; }"); test("BEGIN { let $a: int16 = 1; }"); test(R"(BEGIN { let $a: string; $a = "hiya"; })"); test(R"(BEGIN { let $a: string[5] = "hiya"; })"); test("BEGIN { let $a: int16; print($a); }"); test("BEGIN { let $a; print($a); $a = 1; }"); // If the type is specified it's strict in that future assignments // need to fit into that type test(R"(BEGIN { let $a: string[5] = "hiya"; $a = "bye"; })"); test(R"(BEGIN { let $a = "hiya"; $a = "longerstr"; })"); test("BEGIN { let $a: int16 = 1; $a = (int8)2; }"); // Test more types test("BEGIN { let $a: struct x; }"); test("BEGIN { let $a: struct x *; }"); test("BEGIN { let $a: struct task_struct *; $a = curtask; }"); test("BEGIN { let $a: struct Foo[10]; }"); test("BEGIN { if (1) { let $x; } $x = 2; }"); test("BEGIN { if (1) { let $x; } else { let $x; } let $x; }"); // https://github.com/bpftrace/bpftrace/pull/3668#issuecomment-2596432923 test_for_warning("BEGIN { let $a; print($a); $a = 1; }", "Variable used before it was assigned:"); test_error("BEGIN { let $a; let $a; }", R"( stdin:1:17-23: ERROR: Variable $a was already declared. Variable shadowing is not allowed. BEGIN { let $a; let $a; } ~~~~~~ stdin:1:9-15: ERROR: Initial declaration BEGIN { let $a; let $a; } ~~~~~~ )"); test_error("BEGIN { let $a: uint16; $a = -1; }", R"( stdin:1:26-33: ERROR: Type mismatch for $a: trying to assign value of type 'int64' when variable already has a type 'uint16' BEGIN { let $a: uint16; $a = -1; } ~~~~~~~ )"); test_error("BEGIN { let $a; $a = (uint8)1; $a = -1; }", R"( stdin:1:32-39: ERROR: Type mismatch for $a: trying to assign value of type 'int64' when variable already contains a value of type 'uint8' BEGIN { let $a; $a = (uint8)1; $a = -1; } ~~~~~~~ )"); test_error("BEGIN { let $a: int8; $a = 10000; }", R"( stdin:1:24-34: ERROR: Type mismatch for $a: trying to assign value '10000' which does not fit into the variable of type 'int8' BEGIN { let $a: int8; $a = 10000; } ~~~~~~~~~~ )"); test_error("BEGIN { $a = -1; let $a; }", R"( stdin:1:18-24: ERROR: Variable declarations need to occur before variable usage or assignment. Variable: $a BEGIN { $a = -1; let $a; } ~~~~~~ )"); test_error("BEGIN { let $a: uint16 = -1; }", R"( stdin:1:9-29: ERROR: Type mismatch for $a: trying to assign value of type 'int64' when variable already has a type 'uint16' BEGIN { let $a: uint16 = -1; } ~~~~~~~~~~~~~~~~~~~~ )"); test_error(R"(BEGIN { let $a: string[5] = "hiya"; $a = "longerstr"; })", R"( stdin:1:38-54: ERROR: Type mismatch for $a: trying to assign value of type 'string[10]' when variable already contains a value of type 'string[5]' BEGIN { let $a: string[5] = "hiya"; $a = "longerstr"; } ~~~~~~~~~~~~~~~~ )"); test_error(R"(BEGIN { let $a: sum_t; })", R"( stdin:1:9-23: ERROR: Invalid variable declaration type: sum_t BEGIN { let $a: sum_t; } ~~~~~~~~~~~~~~ )"); test_error(R"(BEGIN { let $a: struct bad_task; $a = *curtask; })", R"( stdin:1:34-47: ERROR: Type mismatch for $a: trying to assign value of type 'struct task_struct' when variable already has a type 'struct bad_task' BEGIN { let $a: struct bad_task; $a = *curtask; } ~~~~~~~~~~~~~ )"); test_error(R"(BEGIN { $x = 2; if (1) { let $x; } })", R"( stdin:1:26-32: ERROR: Variable declarations need to occur before variable usage or assignment. Variable: $x BEGIN { $x = 2; if (1) { let $x; } } ~~~~~~ )"); } TEST(semantic_analyser, block_scoping) { // if/else test("BEGIN { $a = 1; if (1) { $b = 2; print(($a, $b)); } }"); test(R"( BEGIN { $a = 1; if (1) { print(($a)); $b = 2; if (1) { print(($a, $b)); } else { print(($a, $b)); } } })"); // for loops test(R"( BEGIN { @x[0] = 1; $a = 1; for ($kv : @x) { $b = 2; print(($a, $b)); } })"); test(R"( BEGIN { @x[0] = 1; @y[0] = 2; $a = 1; for ($kv : @x) { $b = 2; for ($ap : @y) { print(($a, $b)); } } })"); // while loops test(R"( BEGIN { $a = 1; while (1) { $b = 2; print(($a, $b)); } })"); test(R"( BEGIN { $a = 1; while (1) { print(($a)); $b = 2; while (1) { print(($a, $b)); } } })"); // unroll test("BEGIN { $a = 1; unroll(1) { $b = 2; print(($a, $b)); } }"); test(R"( BEGIN { $a = 1; unroll(1) { $b = 2; unroll(2) { print(($a, $b)); } } })"); // mixed test(R"( BEGIN { $a = 1; @x[0] = 1; if (1) { $b = 2; for ($kv : @x) { $c = 3; while (1) { $d = 4; unroll(1) { $e = 5; print(($a, $b, $c, $d, $e)); } } } } })"); // if/else test_error("BEGIN { if (1) { $a = 1; } print(($a)); }", R"( stdin:1:28-37: ERROR: Undefined or undeclared variable: $a BEGIN { if (1) { $a = 1; } print(($a)); } ~~~~~~~~~ )"); test_error("BEGIN { if (1) { $a = 1; } else { print(($a)); } }", R"( stdin:1:35-44: ERROR: Undefined or undeclared variable: $a BEGIN { if (1) { $a = 1; } else { print(($a)); } } ~~~~~~~~~ )"); test_error("BEGIN { if (1) { $b = 1; } else { $b = 2; } print(($b)); }", R"( stdin:1:45-54: ERROR: Undefined or undeclared variable: $b BEGIN { if (1) { $b = 1; } else { $b = 2; } print(($b)); } ~~~~~~~~~ )"); // for loops test_error( "kprobe:f { @map[0] = 1; for ($kv : @map) { $a = 1; } print(($a)); }", R"( stdin:1:55-64: ERROR: Undefined or undeclared variable: $a kprobe:f { @map[0] = 1; for ($kv : @map) { $a = 1; } print(($a)); } ~~~~~~~~~ )"); // while loops test_error("BEGIN { while (1) { $a = 1; } print(($a)); }", R"( stdin:1:31-40: ERROR: Undefined or undeclared variable: $a BEGIN { while (1) { $a = 1; } print(($a)); } ~~~~~~~~~ )"); // unroll test_error("BEGIN { unroll(1) { $a = 1; } print(($a)); }", R"( stdin:1:31-40: ERROR: Undefined or undeclared variable: $a BEGIN { unroll(1) { $a = 1; } print(($a)); } ~~~~~~~~~ )"); } TEST(semantic_analyser, invalid_assignment) { test_error("BEGIN { @a = hist(10); let $b = @a; }", R"( stdin:1:24-35: ERROR: Map value 'hist_t' cannot be assigned to a scratch variable. BEGIN { @a = hist(10); let $b = @a; } ~~~~~~~~~~~ )"); test_error("BEGIN { @a = lhist(123, 0, 123, 1); let $b = @a; }", R"( stdin:1:37-48: ERROR: Map value 'lhist_t' cannot be assigned to a scratch variable. BEGIN { @a = lhist(123, 0, 123, 1); let $b = @a; } ~~~~~~~~~~~ )"); test_error("BEGIN { @a = stats(10); let $b = @a; }", R"( stdin:1:25-36: ERROR: Map value 'stats_t' cannot be assigned to a scratch variable. BEGIN { @a = stats(10); let $b = @a; } ~~~~~~~~~~~ )"); test_error("BEGIN { @a = hist(10); @b = @a; }", R"( stdin:1:24-31: ERROR: Map value 'hist_t' cannot be assigned from one map to another. The function that returns this type must be called directly e.g. `@b = hist(retval);`. BEGIN { @a = hist(10); @b = @a; } ~~~~~~~ )"); test_error("BEGIN { @a = lhist(123, 0, 123, 1); @b = @a; }", R"( stdin:1:37-44: ERROR: Map value 'lhist_t' cannot be assigned from one map to another. The function that returns this type must be called directly e.g. `@b = lhist(rand %10, 0, 10, 1);`. BEGIN { @a = lhist(123, 0, 123, 1); @b = @a; } ~~~~~~~ )"); test_error("BEGIN { @a = stats(10); @b = @a; }", R"( stdin:1:25-32: ERROR: Map value 'stats_t' cannot be assigned from one map to another. The function that returns this type must be called directly e.g. `@b = stats(arg2);`. BEGIN { @a = stats(10); @b = @a; } ~~~~~~~ )"); } TEST(semantic_analyser, no_maximum_passes) { test("interval:s:1 { @j = @i; @i = @h; @h = @g; @g = @f; @f = @e; @e = @d; " "@d = @c; " "@c = @b; @b = @a; } interval:s:1 { @a = 1; }"); } } // namespace bpftrace::test::semantic_analyser bpftrace-0.23.2/tests/testlibs/000077500000000000000000000000001477746507000164005ustar00rootroot00000000000000bpftrace-0.23.2/tests/testlibs/CMakeLists.txt000066400000000000000000000015251477746507000211430ustar00rootroot00000000000000file(GLOB testlib_sources CONFIGURE_DEPENDS *.c *.cpp) set(testlibtargets "") foreach(testlib_source ${testlib_sources}) get_filename_component(testlib_name ${testlib_source} NAME_WE) add_library(${testlib_name} SHARED ${testlib_source}) set_target_properties(${testlib_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMPILE_FLAGS "-g -O0") if(HAVE_SYSTEMTAP_SYS_SDT_H) target_compile_definitions(${testlib_name} PRIVATE HAVE_SYSTEMTAP_SYS_SDT_H) endif(HAVE_SYSTEMTAP_SYS_SDT_H) # clear the executable bit - ensure bpftrace can trace non-executable # shared objects add_custom_command(TARGET ${testlib_name} POST_BUILD COMMAND chmod -x $) list(APPEND testlibtargets ${testlib_name}) endforeach() add_custom_target(testlibs DEPENDS ${testlibtargets}) bpftrace-0.23.2/tests/testlibs/simple.c000066400000000000000000000000721477746507000200340ustar00rootroot00000000000000__attribute__((__visibility__("default"))) void fun() { } bpftrace-0.23.2/tests/testlibs/usdt_tp.c000066400000000000000000000007531477746507000202330ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE2(a, b, c, d) (void)0 #endif #include "usdt_tp.h" #include #include #include long myclock() { struct timeval tv; gettimeofday(&tv, NULL); DTRACE_PROBE2(tracetestlib, lib_probe_1, tv.tv_sec, "Hello world"); DTRACE_PROBE2(tracetestlib, lib_probe_1, tv.tv_sec, "Hello world2"); DTRACE_PROBE2(tracetestlib2, lib_probe_2, tv.tv_sec, "Hello world3"); return tv.tv_sec; } bpftrace-0.23.2/tests/testlibs/usdt_tp.h000066400000000000000000000001101477746507000202230ustar00rootroot00000000000000#ifndef _USDTLIBX_H_ #define _USDTLIBX_H_ extern long myclock(); #endif bpftrace-0.23.2/tests/testprogs/000077500000000000000000000000001477746507000166015ustar00rootroot00000000000000bpftrace-0.23.2/tests/testprogs/CMakeLists.txt000066400000000000000000000052751477746507000213520ustar00rootroot00000000000000set_property(GLOBAL APPEND_STRING PROPERTY testprog_cflags "-g -O0") # Check and add CFLAG to testprog_cflags function(test_and_add_testprog_cflag flag) try_compile(FLAG_AVAILABLE ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/simple_struct.c LINK_OPTIONS ${flag} ) if(${FLAG_AVAILABLE}) set_property(GLOBAL APPEND_STRING PROPERTY testprog_cflags " ${flag}") else() message(STATUS "${CMAKE_C_COMPILER} does not support ${flag}") endif() endfunction() test_and_add_testprog_cflag("-fno-omit-frame-pointer") test_and_add_testprog_cflag("-mno-omit-leaf-frame-pointer") get_property(testprog_cflags GLOBAL PROPERTY testprog_cflags) file(GLOB testprog_sources CONFIGURE_DEPENDS *.c *.cpp) set(testprogtargets "") foreach(testprog_source ${testprog_sources}) get_filename_component(testprog_name ${testprog_source} NAME_WE) add_executable(${testprog_name} ${testprog_source}) set_target_properties(${testprog_name} PROPERTIES LINK_SEARCH_START_STATIC FALSE LINK_SEARCH_END_STATIC FALSE COMPILE_FLAGS "${testprog_cflags}" LINK_FLAGS "-no-pie") if(HAVE_SYSTEMTAP_SYS_SDT_H) target_compile_definitions(${testprog_name} PRIVATE HAVE_SYSTEMTAP_SYS_SDT_H) endif(HAVE_SYSTEMTAP_SYS_SDT_H) list(APPEND testprogtargets ${testprog_name}) endforeach() find_program(GO_EXECUTABLE go) if(GO_EXECUTABLE) file(GLOB testprog_go_sources CONFIGURE_DEPENDS *.go) foreach(testprog_source ${testprog_go_sources}) get_filename_component(testprog_name ${testprog_source} NAME_WE) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${testprog_name} COMMAND ${GO_EXECUTABLE} build -o ${CMAKE_CURRENT_BINARY_DIR}/${testprog_name} ${testprog_source} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${testprog_source} ) list(APPEND testprogtargets ${testprog_name}) endforeach() endif() find_program(RUSTC_EXECUTABLE rustc) if(RUSTC_EXECUTABLE) file(GLOB testprog_rust_sources CONFIGURE_DEPENDS *.rs) foreach(testprog_source ${testprog_rust_sources}) get_filename_component(testprog_name ${testprog_source} NAME_WE) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${testprog_name} COMMAND ${RUSTC_EXECUTABLE} -C symbol_mangling_version=v0 -o ${CMAKE_CURRENT_BINARY_DIR}/${testprog_name} ${testprog_source} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${testprog_source} ) list(APPEND testprogtargets ${testprog_name}) endforeach() endif() add_custom_target(testprogs ALL DEPENDS ${testprogtargets}) target_include_directories(usdt_lib PRIVATE ${CMAKE_SOURCE_DIR}/tests/testlibs/) target_compile_options(usdt_lib PRIVATE -fPIC) target_link_libraries(usdt_lib usdt_tp) bpftrace-0.23.2/tests/testprogs/array_access.c000066400000000000000000000022421477746507000214040ustar00rootroot00000000000000#include struct A { int x[4]; uint8_t y[4]; }; struct B { int y[2][2]; }; struct C { int *z[4]; }; struct D { int x; int y[0]; int z; }; void test_array(int *a __attribute__((unused))) { } void test_arrays(struct A *a __attribute__((unused)), struct A *d __attribute__((unused))) { } void test_struct(struct A *a __attribute__((unused)), struct B *b __attribute__((unused))) { } void test_ptr_array(struct C *c __attribute__((unused))) { } void test_variable_array(struct D *d __attribute__((unused))) { } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { struct A a; a.x[0] = 1; a.x[1] = 2; a.x[2] = 3; a.x[3] = 4; a.y[0] = 0xaa; a.y[1] = 0xbb; a.y[2] = 0xcc; a.y[3] = 0xdd; struct B b; b.y[0][0] = 5; b.y[0][1] = 6; b.y[1][0] = 7; b.y[1][1] = 8; test_struct(&a, &b); test_array(a.x); struct C c; c.z[0] = &a.x[0]; c.z[1] = &a.x[1]; c.z[2] = &a.x[2]; c.z[3] = &a.x[3]; test_ptr_array(&c); struct A d; d.x[0] = 4; d.x[1] = 3; d.x[2] = 2; d.x[3] = 1; test_arrays(&a, &d); struct D e; e.z = 1; test_variable_array(&e); } bpftrace-0.23.2/tests/testprogs/bitfield_test.c000066400000000000000000000011631477746507000215670ustar00rootroot00000000000000struct Foo { unsigned int a : 4, b : 8, c : 3, d : 1, e : 16; }; struct Bar { unsigned short a : 4, b : 8, c : 3, d : 1; unsigned int e : 9, f : 15, g : 1, h : 2, i : 5; }; __attribute__((noinline)) unsigned int func(struct Foo *foo) { return foo->b; } __attribute__((noinline)) short func2(struct Bar *bar) { return bar->b; } int main() { struct Foo foo; struct Bar bar; foo.a = 1; foo.b = 2; foo.c = 5; foo.d = 0; foo.e = 65535; func(&foo); bar.a = 1; bar.b = 217; bar.c = 5; bar.d = 1; bar.e = 500; bar.f = 31117; bar.g = 1; bar.h = 2; bar.i = 27; func2(&bar); return 0; } bpftrace-0.23.2/tests/testprogs/complex_struct.c000066400000000000000000000010611477746507000220160ustar00rootroot00000000000000#include #include #include struct Foo { char* a; char b[4]; uint8_t c[4]; int d[4]; uint16_t e[4]; uint64_t f[4]; }; void func(struct Foo* foo) { (void)foo; } int main() { struct Foo foo = { .a = malloc(4), .b = { 5, 4, 3, 2 }, .c = { 1, 2, 3, 4 }, .d = { 5, 6, 7, 8 }, .e = { 9, 10, 11, 12 }, .f = { 13, 14, 15, 16 } }; memcpy(foo.a, "\x09\x08\x07\x06", 4); func(&foo); free(foo.a); return 0; } bpftrace-0.23.2/tests/testprogs/disable_aslr.c000066400000000000000000000002541477746507000213720ustar00rootroot00000000000000#include #include int main(int argc __attribute__((unused)), char **argv) { personality(ADDR_NO_RANDOMIZE); execv(argv[1], argv + 1); } bpftrace-0.23.2/tests/testprogs/false.c000066400000000000000000000000371477746507000200370ustar00rootroot00000000000000int main(void) { return 1; } bpftrace-0.23.2/tests/testprogs/fentry_args.c000066400000000000000000000005031477746507000212660ustar00rootroot00000000000000#include #include #include #include int main(int argc, char *argv[]) { if (argc != 3) { return 1; } usleep(1000000); union bpf_attr attr; int cmd = atoi(argv[1]); unsigned int size = atoi(argv[2]); syscall(__NR_bpf, cmd, &attr, size); return 0; } bpftrace-0.23.2/tests/testprogs/hello_go.go000066400000000000000000000000351477746507000207160ustar00rootroot00000000000000package main func main() {} bpftrace-0.23.2/tests/testprogs/hello_rust.rs000066400000000000000000000001051477746507000213230ustar00rootroot00000000000000fn fun1(x: i64) { println!("{}", x) } fn main() { fun1(0) } bpftrace-0.23.2/tests/testprogs/inline_function.c000066400000000000000000000015331477746507000221320ustar00rootroot00000000000000#include /* always_inline instructs the compiler to inline the function, even in -O0. * used instructs the compiler to keep the function in the final binary. * Using both attribute at the same time enable testing both regular and * inlined call to the square function. The first two calls in main will be * inlined and the last call will be a regular call. */ __attribute__((always_inline, used)) static inline int square(int x) { volatile int res = x * x; return res; } int main(int argc, char *argv[] __attribute__((unused))) { printf("%d^2 = %d\n", argc, square(argc)); printf("%d^2 = %d\n", 8, square(8)); // Make a call to `square` through the `square_ptr` pointer, // to prevent the compiler from inlining the call int x = 10; int (*square_ptr)(int) = square; printf("%d^2 = %d\n", x, square_ptr(x)); return 0; } bpftrace-0.23.2/tests/testprogs/intptrcast.c000066400000000000000000000002761477746507000211450ustar00rootroot00000000000000#include int fn(uint16_t nums[]) { return nums[0] + nums[1]; } int main() { uint16_t nums[] = { 0x123, 0x456, 0x789, 0xabc, 0xdef, 0xcba, }; fn(nums); return 0; } bpftrace-0.23.2/tests/testprogs/mountns_pivot_wrapper.c000066400000000000000000000073471477746507000234440ustar00rootroot00000000000000#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #define errExit(msg) \ do { \ perror(msg); \ exit(EXIT_FAILURE); \ } while (0) void mkdir_recursive(const char *path) { char *subpath, *fullpath; fullpath = strdup(path); subpath = dirname(fullpath); if (strlen(subpath) > 1) mkdir_recursive(subpath); if (mkdir(path, 0770) != 0 && (errno != EEXIST)) errExit("Failed to make shared dir mount"); free(fullpath); } /* Run another simple test program in a different mount namespace, to which we have used pivot root. usage: mountns_pivot_wrapper some_testprog This wrapper is very similar to mountns_wrapper, but it pivots the root before executing the program. Docker and containerd use pivot_root to change the root of the running process. This means that when examining the process via /proc on the host, realpath of the executable will report paths in the mounted namespace rather than in the host namespace. Some common directories for system shared libraries are mounted into the new namespace to enable running dynamically linked test executables. */ int main(int argc, char *argv[]) { const char *private_mount = "/tmp/bpftrace-unshare-mountns-pivot-test"; char dpath[PATH_MAX]; char exe[PATH_MAX]; char oldroot[PATH_MAX]; if (argc != 2) errExit("mountns_pivot_wrapper requires and accepts a single argument."); // Enter a new mount namespace if (unshare(CLONE_NEWNS) != 0) errExit("Failed to unshare"); // Recursively set the mount namespace to private, so caller can't see if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) errExit("Failed to make mount private"); // make a tempdir and bind mount containing testprog folder to it if (mkdir(private_mount, 0770) != 0 && (errno != EEXIST)) errExit("Failed to make private mount dir"); int idx = readlink("/proc/self/exe", dpath, sizeof(dpath) - 1); dpath[idx] = '\0'; char *dname = dirname(dpath); if (mount(dname, private_mount, NULL, MS_BIND, NULL) != 0) errExit("Failed to set up private bind mount"); /* * Mount directories containing system libraries, to support running of * dynamically linked programs after we've pivoted our root. */ glob_t globbuf; glob("/lib*", GLOB_NOSORT, NULL, &globbuf); glob("/usr/lib*", GLOB_NOSORT | GLOB_APPEND, NULL, &globbuf); glob("/nix/store", GLOB_NOSORT | GLOB_APPEND, NULL, &globbuf); for (size_t i = 0; i < globbuf.gl_pathc; i++) { const char *global_lib_dir = globbuf.gl_pathv[i]; char shared_dir_mount[PATH_MAX]; snprintf( shared_dir_mount, PATH_MAX, "%s/%s", private_mount, global_lib_dir); mkdir_recursive(shared_dir_mount); if (mount(global_lib_dir, shared_dir_mount, NULL, MS_BIND, NULL) != 0) errExit("Failed to mount a system lib directory"); } /* * Pivot root */ snprintf(oldroot, PATH_MAX, "%s/%s", private_mount, "old"); if (mkdir(oldroot, 0770) != 0 && (errno != EEXIST)) errExit("Failed to make old root directory for pivot_root"); if (syscall(SYS_pivot_root, private_mount, oldroot) != 0) { errExit("Couldn't pivot to new root"); } if (chdir("/") != 0) { errExit("Couldn't chdir to new root"); } snprintf(exe, PATH_MAX, "/%s", argv[1]); char *args[] = { exe, NULL }; return execv(args[0], args); } bpftrace-0.23.2/tests/testprogs/mountns_wrapper.c000066400000000000000000000044541477746507000222170ustar00rootroot00000000000000#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #define errExit(msg) \ do { \ perror(msg); \ exit(EXIT_FAILURE); \ } while (0) /* Run another simple test program in a different mount namespace. usage: mountns_wrapper some_testprog The test program directory is bind-mounted into a path that is private to its mount namespace. bpftrace will run from the caller's mount namespace, before the unshare. This will cause bpftrace to not be able to see the path within its own mount namespace. To access the path, bpftrace must use /proc/PID/root, to see the mount namespace from the target PID's perspective. This is useful for both uprobe and USDT tests, to ensure that bpftrace can target processes running in containers, such as docker. LIMITATIONS: doesn't pass arguments to test program, as this hasn't been necessary yet. */ int main(int argc, char *argv[]) { const char *private_mount = "/tmp/bpftrace-unshare-mountns-test"; char dpath[PATH_MAX]; char exe[PATH_MAX]; if (argc != 2) errExit("Must specify test program as only argument."); // Enter a new mount namespace if (unshare(CLONE_NEWNS) != 0) errExit("Failed to unshare"); // Recursively set the mount namespace to private, so caller can't see if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) errExit("Failed to make mount private"); // make a tempdir and bind mount containing testprog folder to it if (mkdir(private_mount, 0770) != 0 && (errno != EEXIST)) errExit("Failed to make private mount dir"); int idx = readlink("/proc/self/exe", dpath, sizeof(dpath) - 1); dpath[idx] = '\0'; char *dname = dirname(dpath); if (mount(dname, private_mount, NULL, MS_BIND, NULL) != 0) errExit("Failed to set up private bind mount"); snprintf(exe, PATH_MAX, "%s/%s", private_mount, argv[1]); char *args[] = { exe, NULL }; return execv(args[0], args); } bpftrace-0.23.2/tests/testprogs/ptr_to_ptr.c000066400000000000000000000004341477746507000211420ustar00rootroot00000000000000struct Foo { int a; char b[10]; }; int function(struct Foo **f) { return (*f)->a; } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { struct Foo foo1 = { .a = 123, .b = "hello" }; struct Foo *foo2 = &foo1; function(&foo2); return 0; } bpftrace-0.23.2/tests/testprogs/simple_struct.c000066400000000000000000000002501477746507000216370ustar00rootroot00000000000000struct Foo { int m; int n; }; int func(struct Foo *foo) { return foo->m; } int main() { struct Foo foo; foo.m = 2; foo.n = 3; func(&foo); return 0; } bpftrace-0.23.2/tests/testprogs/stack_args.c000066400000000000000000000007161477746507000210720ustar00rootroot00000000000000#define ARG(x) int(x) __attribute((unused)) // Declare enough args that some are placed on the stack void too_many_args(ARG(a), ARG(b), ARG(c), ARG(d), ARG(e), ARG(f), ARG(g), ARG(h), ARG(i), ARG(j)) { } int main(void) { too_many_args(0, 1, 2, 4, 8, 16, 32, 64, 128, 256); return 0; } bpftrace-0.23.2/tests/testprogs/string_args.c000066400000000000000000000002121477746507000212620ustar00rootroot00000000000000void print(char *a, char *b) { (void)a; (void)b; } int main(void) { char sa[] = "hello"; char sb[] = "world"; print(sa, sb); } bpftrace-0.23.2/tests/testprogs/struct_array.c000066400000000000000000000007741477746507000214770ustar00rootroot00000000000000#include #include struct T { uint32_t a; uint32_t b[2]; }; struct W { uint32_t a; struct T t; }; struct C { uint32_t a; void* b; struct W w[10]; }; void clear(struct C* c) { for (int x = 0; x < 10; x++) { c->w[x].t.a = 0; } } int main() { struct C c; c.a = 0x55555555; c.b = (void*)0x55555555; for (int x = 0; x < 10; x++) { c.w[x].a = 100 + x; c.w[x].t.a = x; c.w[x].t.b[0] = 100 - x; c.w[x].t.b[1] = 100 - 2 * x; } clear(&c); } bpftrace-0.23.2/tests/testprogs/struct_walk.c000066400000000000000000000010321477746507000213030ustar00rootroot00000000000000#include #include #include struct C { uint32_t a; uint64_t b; }; void clear(struct C* c, size_t size) { for (size_t t = 0; t < size; t++) { c[t].a = 0; c[t].b = 0; } } void print(struct C* c, size_t size) { uint32_t sum = 0; while (size--) sum += (c++)->a; printf("Sum: %u\n", sum); } int main() { size_t size = 10; struct C* c = (struct C*)malloc(sizeof(struct C) * size); for (size_t t = 0; t < size; t++) { c[t].a = t; c[t].b = 100; } clear(c, size); } bpftrace-0.23.2/tests/testprogs/struct_with_union.c000066400000000000000000000003201477746507000225270ustar00rootroot00000000000000union N { int i; float f; }; struct Foo { int m; union N n; }; int func(struct Foo *foo) { return foo->m; } int main() { struct Foo foo; foo.m = 2; foo.n.i = 5; func(&foo); return 0; } bpftrace-0.23.2/tests/testprogs/syscall.c000066400000000000000000000122511477746507000204200ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include void usage() { printf("Usage:\n"); printf("\t./syscall []\n"); printf("Supported Syscalls:\n"); printf("\t nanosleep [$N] (default args: 100ns)\n"); #if defined(SYS_open) printf("\t open\n"); #endif printf("\t openat\n"); printf("\t read\n"); printf("\t execve [] (at most 128 arguments)\n"); printf("\t connect \n"); } int gen_nanosleep(int argc, char *argv[]) { struct timespec req; const char *arg = argv[2]; req.tv_sec = 0; req.tv_nsec = 100; if (argc > 2) { if (!isdigit(*arg) || !isdigit(arg[strlen(arg) - 1])) { printf("Invalid argument: %s; the argument should be a non-negative " "number with no sign\n", arg); return 1; } double time; char *endptr; time = strtod(arg, &endptr); if (endptr != arg + strlen(arg)) { printf("Argument '%s' should only contain numerial characters\n", arg); return 1; } // if time is less than 1 nsec, round up to 1 nsec, as with sleep command if (time > 0 && time < 1) { time = 1; } req.tv_sec = (int)(time / 1e9); req.tv_nsec = (int)(time - req.tv_sec * 1e9); } int r = syscall(SYS_nanosleep, &req, NULL); if (r) { perror("Error in syscall nanosleep"); return 1; } return 0; } // the returned string was created with malloc() // @ path_suffix should start with '/' char *get_tmp_file_path(const char *path_suffix) { const char *tmpdir = getenv("TMPDIR"); if (tmpdir == NULL) { tmpdir = "/tmp"; } int path_len = strlen(tmpdir) + strlen(path_suffix); char *path = (char *)malloc((path_len + 1) * sizeof(char)); memset(path, '\0', path_len + 1); strncat(path, tmpdir, strlen(tmpdir)); strncat(path, path_suffix, strlen(path_suffix)); return path; } int gen_open_openat(bool is_sys_open) { char *file_path = get_tmp_file_path( "/bpftrace_runtime_test_syscall_gen_open_temp"); int fd = -1; if (is_sys_open) { #if defined(SYS_open) fd = syscall(SYS_open, file_path, O_CREAT); #endif } else { fd = syscall(SYS_openat, AT_FDCWD, file_path, O_CREAT); } if (fd < 0) { perror("Error in syscall open/openat"); free(file_path); return 1; } close(fd); remove(file_path); free(file_path); return 0; } int gen_read() { char *file_path = get_tmp_file_path( "/bpftrace_runtime_test_syscall_gen_read_temp"); int fd = open(file_path, O_CREAT, 0644); if (fd < 0) { perror("Error in syscall read when creating temp file"); free(file_path); return 1; } char buf[10]; int r = syscall(SYS_read, fd, (void *)buf, 0); close(fd); remove(file_path); free(file_path); if (r < 0) { perror("Error in syscall read"); return 1; } return 0; } int gen_execve(int argc, char *argv[]) { if (argc < 3) { printf("Indicate which process to execute.\n"); return 1; } char *newargv[129] = { NULL }; char *newenv[] = { NULL }; newargv[0] = argv[2]; int arg_nm = argc - 3; if (arg_nm > 128) { printf("Too many arguments: at most 128 arguments, %d given\n", arg_nm); return 1; } for (int i = 0; i < argc; ++i) { newargv[1 + i] = argv[3 + i]; } syscall(SYS_execve, argv[2], newargv, newenv); // execve returns on error perror("Error in syscall execve"); return 1; } int gen_connect(int argc, char *argv[]) { if (argc < 3) { printf("Indicate which host and port to connect.\n"); return 1; } int socket_desc; struct sockaddr_in server_addr; socket_desc = socket(AF_INET, SOCK_STREAM, 0); if (socket_desc < 0) { printf("Unable to create socket\n"); return 1; } int port; if (sscanf(argv[3], "%d", &port) <= 0) { printf("Argument invalid\n"); return 1; } if (port < 1 || port > 65535) { printf("Argument '%s' is out of range, should be in [1, 65535]\n", argv[3]); return 1; } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = inet_addr(argv[2]); connect(socket_desc, (struct sockaddr *)&server_addr, sizeof(server_addr)); close(socket_desc); return 0; } int main(int argc, char *argv[]) { if (argc < 2) { usage(); return 1; } const char *syscall_name = argv[1]; int r = 0; if (strcmp("--help", syscall_name) == 0 || strcmp("-h", syscall_name) == 0) { usage(); } else if (strcmp("nanosleep", syscall_name) == 0) { r = gen_nanosleep(argc, argv); } else if (strcmp("openat", syscall_name) == 0) { r = gen_open_openat(false); } #if defined(SYS_open) else if (strcmp("open", syscall_name) == 0) { r = gen_open_openat(true); } #endif else if (strcmp("read", syscall_name) == 0) { r = gen_read(); } else if (strcmp("execve", syscall_name) == 0) { r = gen_execve(argc, argv); } else if (strcmp("connect", syscall_name) == 0) { r = gen_connect(argc, argv); } else { printf("%s is not supported yet\n", syscall_name); usage(); r = 1; } return r; } bpftrace-0.23.2/tests/testprogs/systemtap_sys_sdt_check.c000066400000000000000000000003231477746507000237010ustar00rootroot00000000000000int main() { // If we don't have Systemtap headers, we should skip USDT tests. Returning 1 // can be used as validation in the REQUIRE #ifndef HAVE_SYSTEMTAP_SYS_SDT_H return 1; #else return 0; #endif } bpftrace-0.23.2/tests/testprogs/true.c000066400000000000000000000000371477746507000177240ustar00rootroot00000000000000int main(void) { return 0; } bpftrace-0.23.2/tests/testprogs/uprobe_fork_loop.c000066400000000000000000000006641477746507000223210ustar00rootroot00000000000000#include #include #include int uprobeFunction1(int *n, char c __attribute__((unused))) { return *n; } void spin() { while (1) { int n = 13; char c = 'x'; uprobeFunction1(&n, c); usleep(500); } } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { pid_t p = fork(); if (p < 0) { perror("fork fail"); exit(1); } spin(); return 0; } bpftrace-0.23.2/tests/testprogs/uprobe_library.c000066400000000000000000000003121477746507000217610ustar00rootroot00000000000000#include int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { int val; FILE *fd = fopen("/dev/zero", "r"); fread(&val, sizeof(int), 1, fd); fclose(fd); } bpftrace-0.23.2/tests/testprogs/uprobe_loop.c000066400000000000000000000004771477746507000213020ustar00rootroot00000000000000#include int uprobeFunction1(int *n, char c __attribute__((unused))) { return *n; } void spin() { while (1) { int n = 13; char c = 'x'; uprobeFunction1(&n, c); usleep(500); } } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { spin(); return 0; } bpftrace-0.23.2/tests/testprogs/uprobe_namesan.c000066400000000000000000000001301477746507000217350ustar00rootroot00000000000000int __attribute__((noinline)) fn$() { return 0; } int main(void) { return fn$(); } bpftrace-0.23.2/tests/testprogs/uprobe_negative_retval.c000066400000000000000000000003641477746507000235030ustar00rootroot00000000000000#include __attribute__((noinline)) int function1(int x) { return x; } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { for (int x = -120; x <= 100; x++) { function1(x); } return -100; } bpftrace-0.23.2/tests/testprogs/uprobe_symres.c000066400000000000000000000002561477746507000216460ustar00rootroot00000000000000#include __attribute__((noinline)) int test() { return 42; } __attribute__((noinline)) int test2() { return test(); } int main() { test2(); sleep(5); } bpftrace-0.23.2/tests/testprogs/uprobe_symres_exited_process.c000066400000000000000000000002561477746507000247460ustar00rootroot00000000000000#include __attribute__((noinline)) int test() { return 42; } __attribute__((noinline)) int test2() { return test(); } int main() { sleep(3); test2(); } bpftrace-0.23.2/tests/testprogs/uprobe_test.c000066400000000000000000000043371477746507000213070ustar00rootroot00000000000000#include int GLOBAL_A = 0x55555555; int GLOBAL_B = 0x88888888; int GLOBAL_C = 0x33333333; char GLOBAL_D = 8; struct Foo { int a; char b[10]; int c[3]; __int128_t d; }; int uprobeFunction1(int *n, char c __attribute__((unused))) { return *n; } struct Foo *uprobeFunction2(struct Foo *foo1, struct Foo *foo2 __attribute__((unused))) { return foo1; } // clang-format off /* * Anonymous types inside parameter lists are not legal until C23 [0][1][2]. * On a non-C23 build, we get unsilencable compiler warnings: * * /home/dxu/dev/bpftrace/tests/testprogs/uprobe_test.c:26:10: warning: anonymous enum declared inside parameter list will not be visible outside of this definition or declaration * 26 | enum { A, B, C } e, * | ^ * [4/5] Linking C executable tests/testprogs/uprobe_test * * So for now, leave this commented out until C23 is more widely available. * * [0]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108189 * [1]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3037.pdf * [2]: https://www.reddit.com/r/C_Programming/comments/w5hl80/comment/ih8jxi6/?context=3 * * int uprobeFunction3( * enum { A, B, C } e, * union { * int a; * char b; * } u __attribute__((unused))) * { * return e; * } */ // clang-format on __uint128_t uprobeFunctionUint128(__uint128_t x, __uint128_t y __attribute__((unused)), __uint128_t z __attribute__((unused)), __uint128_t w) { return x + w; } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { while (1) { int n = 13; char c = 'x'; uprobeFunction1(&n, c); struct Foo foo1 = { .a = 123, .b = "hello", .c = { 1, 2, 3 }, .d = 0x123456789ABCDEF0 }; struct Foo foo2 = { .a = 456, .b = "world", .c = { 4, 5, 6 }, .d = 0xFEDCBA9876543210 }; uprobeFunction2(&foo1, &foo2); __uint128_t x = 0x123456789ABCDEF0; __uint128_t y = 0xEFEFEFEFEFEFEFEF; __uint128_t z = 0xCDCDCDCDCDCDCDCD; __uint128_t w = 0xABABABABABABABAB; uprobeFunctionUint128(x, y, z, w); usleep(100000); } return 0; } bpftrace-0.23.2/tests/testprogs/uprobe_test_cxx.cpp000066400000000000000000000010121477746507000225140ustar00rootroot00000000000000#include struct Foo { int a, b, c; int &x; }; class Bar { public: int x, y, z; }; int uprobeFunction1(int &x, Foo &foo, Bar &bar __attribute__((unused))) { return x + foo.c; } int uprobeArray(int (&array)[10]) { return array[0]; } int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { int x = 42; Foo foo{ 1, 2, 3, x }; Bar bar{ 10, 11, 12 }; uprobeFunction1(x, foo, bar); int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; uprobeArray(arr); return 0; } bpftrace-0.23.2/tests/testprogs/usdt_args.c000066400000000000000000000110031477746507000207330ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE1(a, b, d) (void)0 #endif #include #include /* For best results compile using gcc -O1 (or higher) * * Clang likes to always put the argument on stack rather than outputting an * operand to access it directly. * * Note: The macros might not always output the type of probe claimed as this * varies depending on compiler and optimisation settings. * * So I've used macros to make the repeated code easier to fix in the * future. */ #if (defined(__GNUC__) && !defined(__clang__)) #pragma GCC optimize("O1") #else #pragma message("non-gcc compiler: the correct probes might not be installed") #endif #define test_value 0xf7f6f5f4f3f2f1f0 volatile uint64_t one = 1; typedef union all_types { int8_t i_8; uint8_t i_u8; uint16_t i_u16; int16_t i_16; uint32_t i_u32; int32_t i_32; uint64_t i_u64; int64_t i_64; } all_types_t; #ifdef __x86_64__ /* Force the value to be stored in a register, works for both gcc and clang * * PROBE_REG(al, u, 8) expands to: * register uint8_t _reg_u8 asm("al") = (uint8_t)test_value; * DTRACE_PROBE1(usdt_args, reg_u8, _reg_u8) */ #define PROBE_REG(register_, sign, size) \ register sign##int##size##_t _reg_##sign##size asm( \ #register_) = (sign##int##size##_t)test_value; \ DTRACE_PROBE1(usdt_args, reg_##sign##size, _reg_##sign##size) #else /* Force a calculation to get the value into a register * works for gcc (-O1+) but not clang * * PROBE_REG(al, u, 8) expands to: * uint8_t _reg_u8 = (uint8_t)(test_value * one); * DTRACE_PROBE1(usdt_args, reg_u8, _reg_u8); * */ #define PROBE_REG(register_, sign, size) \ sign##int##size##_t _reg_##sign##size = (sign##int##size##_t)(test_value * \ one); \ DTRACE_PROBE1(usdt_args, reg_##sign##size, _reg_##sign##size) #endif /* Read a value of the stack, expect an offset here * Works with optimisation -O1+ * * PROBE_ADDRESS(u, 8) expands to: * array[1].i_u8 = (uint8_t) test_value; * DTRACE_PROBE1(usdt_args, addr_u8, array[1].i_u8) */ #define PROBE_ADDRESS(sign, size) \ array[1].i_##sign##size = (sign##int##size##_t)test_value; \ DTRACE_PROBE1(usdt_args, addr_##sign##size, array[1].i_##sign##size) /* Read a value of the stack, expect an offset here * Works only with gcc optimisation -O1+ * * PROBE_INDEX(u, 8) expands to: * DTRACE_PROBE1(usdt_args, index_u8, array[i].i_u8) */ #define PROBE_INDEX(sign, size) \ DTRACE_PROBE1(usdt_args, index_##sign##size, array[i].i_##sign##size) int main() { volatile all_types_t array[10]; for (volatile size_t i = 0; i < sizeof(array) / sizeof(array[0]); i++) { array[i].i_u64 = test_value; } #ifdef HAVE_SYSTEMTAP_SYS_SDT_H /* Constants */ DTRACE_PROBE1(usdt_args, const_u64, (uint64_t)test_value); DTRACE_PROBE1(usdt_args, const_64, (int64_t)test_value); DTRACE_PROBE1(usdt_args, const_u32, (uint32_t)test_value); DTRACE_PROBE1(usdt_args, const_32, (int32_t)test_value); DTRACE_PROBE1(usdt_args, const_u16, (uint16_t)test_value); DTRACE_PROBE1(usdt_args, const_16, (int16_t)test_value); DTRACE_PROBE1(usdt_args, const_u8, (uint8_t)test_value); DTRACE_PROBE1(usdt_args, const_8, (int8_t)test_value); /* Direct register reads - start from 64 and work down * to verify the correct number of bytes are read */ PROBE_REG(rax, u, 64); PROBE_REG(rax, , 64); PROBE_REG(eax, u, 32); PROBE_REG(eax, , 32); PROBE_REG(ax, u, 16); PROBE_REG(ax, , 16); PROBE_REG(al, u, 8); PROBE_REG(al, , 8); /* Base address most likely with and offset(aka. displacement) */ PROBE_ADDRESS(u, 64); PROBE_ADDRESS(, 64); PROBE_ADDRESS(u, 32); PROBE_ADDRESS(, 32); PROBE_ADDRESS(u, 16); PROBE_ADDRESS(, 16); PROBE_ADDRESS(u, 8); PROBE_ADDRESS(, 8); /* Base address + offset + (index * scale) */ for (volatile int i = 7; i <= 7; i++) { PROBE_INDEX(u, 64); PROBE_INDEX(, 64); PROBE_INDEX(u, 32); PROBE_INDEX(, 32); PROBE_INDEX(u, 16); PROBE_INDEX(, 16); PROBE_INDEX(u, 8); PROBE_INDEX(, 8); } /* TLS not yet supported, need label and segment support */ #if 0 volatile static __thread uint64_t tls_64 = (uint64_t)test_value; DTRACE_PROBE1(usdt_args, tls_u64, tls_64); #endif #endif /* HAVE_SYSTEMTAP_SYS_SDT_H */ return 0; } bpftrace-0.23.2/tests/testprogs/usdt_inlined.c000066400000000000000000000013021477746507000214220ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE2(a, b, c, d) (void)0 #endif #include #include #include __attribute__((always_inline)) inline static void myclock(int probe_num) { // Volatile forces reading directly from the stack so that // the probe's argument is not saved as a constant value. volatile int on_stack = probe_num; (void)on_stack; struct timeval tv; gettimeofday(&tv, NULL); DTRACE_PROBE2(tracetest, testprobe, tv.tv_sec, on_stack); } static void mywrapper() { myclock(100); } static void loop() { while (1) { myclock(999); mywrapper(); sleep(1); } } int main() { loop(); return 0; } bpftrace-0.23.2/tests/testprogs/usdt_lib.c000066400000000000000000000001451477746507000205520ustar00rootroot00000000000000#include "usdt_tp.h" #include int main() { while (1) { myclock(); } return 0; } bpftrace-0.23.2/tests/testprogs/usdt_multiple_locations.c000066400000000000000000000013011477746507000237050ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE2(a, b, c, d) (void)0 #endif #include #include static long myclock() { struct timeval tv; gettimeofday(&tv, NULL); DTRACE_PROBE2(tracetest, testprobe, tv.tv_sec, "Hello world"); DTRACE_PROBE2(tracetest, testprobe, tv.tv_sec, "Hello world2"); DTRACE_PROBE2(tracetest, testprobe2, tv.tv_sec, "Hello world3"); DTRACE_PROBE2(tracetest, testprobe3, tv.tv_sec, "Hello world4"); DTRACE_PROBE2(tracetest, testprobe3, tv.tv_sec, "Hello world5"); DTRACE_PROBE2(tracetest, testprobe3, tv.tv_sec, "Hello world6"); return tv.tv_sec; } int main() { while (1) { myclock(); } return 0; } bpftrace-0.23.2/tests/testprogs/usdt_quoted_probe.c000066400000000000000000000004351477746507000224760ustar00rootroot00000000000000#include #ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE1(a, b, c) (void)0 #endif int main() { int a = 1; // For some reason some compilers think `a` is unused (void)a; while (1) DTRACE_PROBE1(test, "probe1", a); return 0; } bpftrace-0.23.2/tests/testprogs/usdt_semaphore_test.c000066400000000000000000000014161477746507000230300ustar00rootroot00000000000000#define _SDT_HAS_SEMAPHORES 1 #ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE2(a, b, c, d) (void)0 #endif #include #include #include __extension__ unsigned short tracetest_testprobe_semaphore __attribute__((unused)) __attribute__((section(".probes"))) __attribute__((visibility("hidden"))); static long myclock() { char buffer[100]; struct timeval tv; gettimeofday(&tv, NULL); sprintf(buffer, "tracetest_testprobe_semaphore: %d\n", tracetest_testprobe_semaphore); DTRACE_PROBE2(tracetest, testprobe, tv.tv_sec, buffer); return tv.tv_sec; } int main() { while (1) { myclock(); // Sleep is necessary to not overflow perf buffer usleep(1000); } return 0; } bpftrace-0.23.2/tests/testprogs/usdt_sized_args.c000066400000000000000000000006021477746507000221340ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE1(a, b, c) (void)0 #endif #include int main() { uint32_t a = 0xdeadbeef; uint32_t b = 1; uint64_t c = UINT64_MAX; (void)a; (void)b; (void)c; while (1) { DTRACE_PROBE1(test, probe1, a); DTRACE_PROBE1(test, probe2, b); DTRACE_PROBE1(test, probe3, c); } return 0; } bpftrace-0.23.2/tests/testprogs/usdt_test.c000066400000000000000000000010151477746507000207600ustar00rootroot00000000000000#ifdef HAVE_SYSTEMTAP_SYS_SDT_H #include #else #define DTRACE_PROBE2(a, b, c, d) (void)0 #endif #include #include #include static long myclock() { struct timeval tv; gettimeofday(&tv, NULL); DTRACE_PROBE2(tracetest, testprobe, tv.tv_sec, "Hello world"); DTRACE_PROBE2(tracetest, testprobe2, tv.tv_sec, "Hello world2"); DTRACE_PROBE2(tracetest2, testprobe2, tv.tv_sec, "Hello world3"); return tv.tv_sec; } int main() { while (1) { myclock(); } return 0; } bpftrace-0.23.2/tests/testprogs/wait10.c000066400000000000000000000001011477746507000200420ustar00rootroot00000000000000#include int main(void) { sleep(10); return 0; } bpftrace-0.23.2/tests/testprogs/wait4_ru.c000066400000000000000000000004371477746507000205070ustar00rootroot00000000000000#include #include #include #include #include #include int main(void) { struct rusage rusage; pid_t pid; pid = fork(); if (pid == 0) { exit(0); } wait4(pid, NULL, 0, &rusage); return 0; } bpftrace-0.23.2/tests/testprogs/watchpoint.c000066400000000000000000000015151477746507000211270ustar00rootroot00000000000000#include #include #include #include #include int main() { volatile void* addr = mmap((void*)0x10000000, 2 << 20, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if ((long)addr < 0) { perror("mmap"); return 1; } mode_t old_umask = umask(S_IWGRP | S_IROTH | S_IWOTH); FILE* addr_fp = fopen("/tmp/watchpoint_mem", "w"); if (!addr_fp) perror("failed to open file in /tmp"); fprintf(addr_fp, "%p", addr); fclose(addr_fp); uint8_t i = 0; while (i < 10) { *((volatile uint8_t*)addr) = i++; // 250ms*10 sleep, enough for watchpoint trigger usleep(250 * 1000); } umask(old_umask); } bpftrace-0.23.2/tests/testprogs/watchpoint_func.c000066400000000000000000000004171477746507000221420ustar00rootroot00000000000000#include #include #include __attribute__((noinline)) void increment(__attribute__((unused)) int _, int *i) { (*i)++; } int main() { int *i = malloc(sizeof(int)); while (1) { increment(0, i); (*i)++; usleep(1000); } } bpftrace-0.23.2/tests/testprogs/watchpoint_func_many_probes.c000066400000000000000000000003241477746507000245350ustar00rootroot00000000000000#include #include #include __attribute__((noinline)) void increment(int *i) { (*i)++; } int main() { for (int i = 0; i < 20; ++i) { increment(malloc(sizeof(int))); } } bpftrace-0.23.2/tests/testprogs/watchpoint_func_wildcard.c000066400000000000000000000020601477746507000240070ustar00rootroot00000000000000#include #include #include #define GEN_FUNC(x) \ __attribute__((noinline)) void increment_##x(int *i) \ { \ (*i)++; \ } #define CALL_FUNC(x) increment_##x(malloc(sizeof(int))) GEN_FUNC(0) GEN_FUNC(1) GEN_FUNC(2) GEN_FUNC(3) GEN_FUNC(4) GEN_FUNC(5) GEN_FUNC(6) GEN_FUNC(7) GEN_FUNC(8) GEN_FUNC(9) GEN_FUNC(10) GEN_FUNC(11) GEN_FUNC(12) GEN_FUNC(13) GEN_FUNC(14) GEN_FUNC(15) GEN_FUNC(16) GEN_FUNC(17) GEN_FUNC(18) GEN_FUNC(19) GEN_FUNC(20) int main() { CALL_FUNC(0); CALL_FUNC(1); CALL_FUNC(2); CALL_FUNC(3); CALL_FUNC(4); CALL_FUNC(5); CALL_FUNC(6); CALL_FUNC(7); CALL_FUNC(8); CALL_FUNC(9); CALL_FUNC(10); CALL_FUNC(11); CALL_FUNC(12); CALL_FUNC(13); CALL_FUNC(14); CALL_FUNC(15); CALL_FUNC(16); CALL_FUNC(17); CALL_FUNC(18); CALL_FUNC(19); CALL_FUNC(20); } bpftrace-0.23.2/tests/testprogs/watchpoint_unwatch.c000066400000000000000000000005171477746507000226610ustar00rootroot00000000000000#include #include #include __attribute__((noinline)) void increment(int *i) { (*i)++; } int main() { int *i = malloc(sizeof(int)); increment(i); // Yes, this sleep sucks but unwatch is async and we have // no way to delay the bpf prog sleep(1); (*i)++; (*i)++; (*i)++; (*i)++; } bpftrace-0.23.2/tests/tools-parsing-test.sh000077500000000000000000000025601477746507000206670ustar00rootroot00000000000000#!/usr/bin/env bash set -u set +e; if [[ $EUID -ne 0 ]]; then >&2 echo "Must be run as root" exit 1 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 set_tooldir() { local dir for dir in "${DIR}/../../tools"; do if [[ -d "$dir" ]]; then TOOLDIR="$dir" return fi done >&2 echo "Tool dir not found" exit 1 } function do_test() { local file="$1" if $BPFTRACE_EXECUTABLE --unsafe -v --dry-run "$file" 2>/dev/null >/dev/null; then echo "$file passed" else echo "$file failed"; $BPFTRACE_EXECUTABLE --unsafe -v --dry-run "$file"; EXIT_STATUS=1; fi } 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 bpftrace-0.23.2/tests/tracepoint_format_parser.cpp000066400000000000000000000246201477746507000223530ustar00rootroot00000000000000#include "tracepoint_format_parser.h" #include "mocks.h" #include "gtest/gtest.h" #include using namespace testing; namespace bpftrace::test::tracepoint_format_parser { class MockTracepointFormatParser : public TracepointFormatParser { public: static std::string get_tracepoint_struct_public(std::istream &format_file, const std::string &category, const std::string &event_name, BPFtrace &bpftrace) { return get_tracepoint_struct(format_file, category, event_name, bpftrace); } }; TEST(tracepoint_format_parser, tracepoint_struct) { std::string input = "name: sys_enter_read\n" "ID: 650\n" "format:\n" " field:unsigned short common_type; offset:0; size:2; " "signed:0;\n" " field:unsigned char common_flags; offset:2; size:1; " "signed:0;\n" " field:unsigned char common_preempt_count; offset:3; " "size:1; signed:0;\n" " field:int common_pid; offset:4; size:4; signed:1;\n" "\n" " field:int __syscall_nr; offset:8; size:4; signed:1;\n" " field:unsigned int fd; offset:16; size:8; signed:0;\n" " field:char * buf; offset:24; size:8; signed:0;\n" " field:size_t count; offset:32; size:8; signed:0;\n" "\n" "print fmt: \"fd: 0x%08lx, buf: 0x%08lx, count: 0x%08lx\", ((unsigned " "long)(REC->fd)), ((unsigned long)(REC->buf)), ((unsigned " "long)(REC->count))\n"; std::string expected = "struct _tracepoint_syscalls_sys_enter_read\n" "{\n" " unsigned short common_type;\n" " unsigned char common_flags;\n" " unsigned char common_preempt_count;\n" " int common_pid;\n" " int __syscall_nr;\n" " char __pad_12;\n" " char __pad_13;\n" " char __pad_14;\n" " char __pad_15;\n" " u64 fd;\n" " char * buf;\n" " size_t count;\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, array) { std::string input = " field:char char_array[8]; offset:0; size:8; signed:1;\n" " field:int int_array[2]; offset:8; size:8; signed:1;\n"; std::string expected = "struct _tracepoint_syscalls_sys_enter_read\n" "{\n" " char char_array[8];\n" " int int_array[2];\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, data_loc) { std::string input = " field:__data_loc char[] msg; offset:8; size:4; signed:1;"; std::string expected = "struct _tracepoint_syscalls_sys_enter_read\n" "{\n" " __attribute__((annotate(\"tp_data_loc\"))) int msg;\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, adjust_integer_types) { std::string input = " field:int arr[8]; offset:0; size:32; signed:1;\n" " field:int int_a; offset:0; size:4; signed:1;\n" " field:int int_b; offset:0; size:8; signed:1;\n" " field:u32 u32_a; offset:0; size:4; signed:0;\n" " field:u32 u32_b; offset:0; size:8; signed:0;\n" " field:unsigned int uint_a; offset:0; size:4; signed:0;\n" " field:unsigned int uint_b; offset:0; size:8; signed:0;\n" " field:unsigned unsigned_a; offset:0; size:4; signed:0;\n" " field:unsigned unsigned_b; offset:0; size:8; signed:0;\n" " field:uid_t uid_a; offset:0; size:4; signed:0;\n" " field:uid_t uid_b; offset:0; size:8; signed:0;\n" " field:gid_t gid_a; offset:0; size:4; signed:0;\n" " field:gid_t gid_b; offset:0; size:8; signed:0;\n" " field:pid_t pid_a; offset:0; size:4; signed:1;\n" " field:pid_t pid_b; offset:0; size:8; signed:0;\n"; std::string expected = "struct _tracepoint_syscalls_sys_enter_read\n" "{\n" " int arr[8];\n" " int int_a;\n" " s64 int_b;\n" " u32 u32_a;\n" " u64 u32_b;\n" " unsigned int uint_a;\n" " u64 uint_b;\n" " unsigned unsigned_a;\n" " u64 unsigned_b;\n" " uid_t uid_a;\n" " u64 uid_b;\n" " gid_t gid_a;\n" " u64 gid_b;\n" " pid_t pid_a;\n" " u64 pid_b;\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, padding) { std::string input = " field:unsigned short common_type; offset:0; size:2; " "signed:0;\n" " field:unsigned char common_flags; offset:2; size:1; " "signed:0;\n" " field:unsigned char common_preempt_count; offset:3; " "size:1; signed:0;\n" " field:int common_pid; offset:4; size:4; signed:1;\n" " field:unsigned char common_migrate_disable; offset:8; " "size:1; signed:0;\n" " field:unsigned char common_preempt_lazy_count; offset:9; " "size:1; signed:0;\n" " field:char comm[16]; offset:12; size:16; signed:1;\n" " field:pid_t pid; offset:28; size:4; signed:1;\n" " field:int prio; offset:32; size:4; signed:1;\n" " field:int success; offset:36; size:4; signed:1;\n" " field:int target_cpu; offset:40; size:4; signed:1;\n"; std::string expected = "struct _tracepoint_sched_sched_wakeup\n" "{\n" " unsigned short common_type;\n" " unsigned char common_flags;\n" " unsigned char common_preempt_count;\n" " int common_pid;\n" " unsigned char common_migrate_disable;\n" " unsigned char common_preempt_lazy_count;\n" " char __pad_10;\n" " char __pad_11;\n" " char comm[16];\n" " pid_t pid;\n" " int prio;\n" " int success;\n" " int target_cpu;\n" "};\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "sched", "sched_wakeup", bpftrace); EXPECT_EQ(expected, result); } TEST(tracepoint_format_parser, tracepoint_struct_btf) { std::string input = "name: sys_enter_read\n" "ID: 650\n" "format:\n" " field:unsigned short common_type; offset:0; size:2; " "signed:0;\n" " field:unsigned char common_flags; offset:2; size:1; " "signed:0;\n" " field:unsigned char common_preempt_count; offset:3; " "size:1; signed:0;\n" " field:int common_pid; offset:4; size:4; signed:1;\n" "\n" " field:int __syscall_nr; offset:8; size:4; signed:1;\n" " field:unsigned int fd; offset:16; size:8; signed:0;\n" " field:char * buf; offset:24; size:8; signed:0;\n" " field:size_t count; offset:32; size:8; signed:0;\n" " field:char comm[TASK_COMM_LEN]; offset:40; size:16; signed:1;\n" "\n" "print fmt: \"fd: 0x%08lx, buf: 0x%08lx, count: 0x%08lx\", ((unsigned " "long)(REC->fd)), ((unsigned long)(REC->buf)), ((unsigned " "long)(REC->count))\n"; std::istringstream format_file(input); MockBPFtrace bpftrace; std::string result = MockTracepointFormatParser::get_tracepoint_struct_public( format_file, "syscalls", "sys_enter_read", bpftrace); // Check that BTF types are populated EXPECT_THAT(bpftrace.btf_set_, Contains("unsigned short")); EXPECT_THAT(bpftrace.btf_set_, Contains("unsigned char")); EXPECT_THAT(bpftrace.btf_set_, Contains("int")); EXPECT_THAT(bpftrace.btf_set_, Contains("u64")); EXPECT_THAT(bpftrace.btf_set_, Contains("char *")); EXPECT_THAT(bpftrace.btf_set_, Contains("size_t")); EXPECT_THAT(bpftrace.btf_set_, Contains("char")); EXPECT_THAT(bpftrace.btf_set_, Contains("TASK_COMM_LEN")); } TEST(tracepoint_format_parser, args_field_access) { // Test computing the level of nested structs accessed from tracepoint args BPFtrace bpftrace; Driver driver(bpftrace); ast::TracepointArgsVisitor visitor(driver.ctx); EXPECT_EQ(driver.parse_str("BEGIN { args.f1->f2->f3 }"), 0); visitor.visit(*driver.ctx.root->probes.at(0)); EXPECT_EQ(driver.ctx.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.ctx.root->probes.at(0)); EXPECT_EQ(driver.ctx.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.ctx.root->probes.at(0)); EXPECT_EQ(driver.ctx.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.ctx.root->probes.at(0)); EXPECT_EQ(driver.ctx.root->probes.at(0)->tp_args_structs_level, -1); } } // namespace bpftrace::test::tracepoint_format_parser bpftrace-0.23.2/tests/types.cpp000066400000000000000000000054021477746507000164200ustar00rootroot00000000000000#include "types.h" #include "struct.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace bpftrace::test::types { static std::string to_str(SizedType type) { std::stringstream out; out << type; return out.str(); } TEST(types, to_str) { EXPECT_EQ(to_str(CreateInt8()), "int8"); EXPECT_EQ(to_str(CreateInt16()), "int16"); EXPECT_EQ(to_str(CreateInt32()), "int32"); EXPECT_EQ(to_str(CreateInt64()), "int64"); EXPECT_EQ(to_str(CreateUInt8()), "uint8"); EXPECT_EQ(to_str(CreateUInt16()), "uint16"); EXPECT_EQ(to_str(CreateUInt32()), "uint32"); EXPECT_EQ(to_str(CreateUInt64()), "uint64"); EXPECT_EQ(to_str(CreateInet(10)), "inet[10]"); EXPECT_EQ(to_str(CreateString(10)), "string[10]"); EXPECT_EQ(to_str(CreateBuffer(10)), "buffer[14]"); // metadata headroom EXPECT_EQ(to_str(CreatePointer(CreateInt8(), AddrSpace::kernel)), "int8 *"); auto ptr_ctx = CreatePointer(CreateInt8(), AddrSpace::kernel); ptr_ctx.MarkCtxAccess(); EXPECT_EQ(to_str(ptr_ctx), "(ctx) int8 *"); EXPECT_EQ(to_str(CreateReference(CreateInt8(), AddrSpace::kernel)), "int8 &"); EXPECT_EQ(to_str(CreateArray(2, CreateInt8())), "int8[2]"); auto record = std::weak_ptr(); EXPECT_EQ(to_str(CreateRecord("hello", record)), "hello"); std::shared_ptr tuple = Struct::CreateTuple( { CreateInt8(), CreateString(10) }); EXPECT_EQ(to_str(CreateTuple(tuple)), "(int8,string[10])"); EXPECT_EQ(to_str(CreateSum(true)), "sum_t"); EXPECT_EQ(to_str(CreateSum(false)), "usum_t"); EXPECT_EQ(to_str(CreateMin(true)), "min_t"); EXPECT_EQ(to_str(CreateMin(false)), "umin_t"); EXPECT_EQ(to_str(CreateMax(true)), "max_t"); EXPECT_EQ(to_str(CreateMax(false)), "umax_t"); EXPECT_EQ(to_str(CreateAvg(true)), "avg_t"); EXPECT_EQ(to_str(CreateAvg(false)), "uavg_t"); EXPECT_EQ(to_str(CreateStats(true)), "stats_t"); EXPECT_EQ(to_str(CreateStats(false)), "ustats_t"); EXPECT_EQ(to_str(CreateCount(true)), "count_t"); EXPECT_EQ(to_str(CreateCount(false)), "ucount_t"); EXPECT_EQ(to_str(CreateMacAddress()), "mac_address"); EXPECT_EQ(to_str(CreateStack(true)), "kstack"); EXPECT_EQ(to_str(CreateStack(false)), "ustack"); EXPECT_EQ(to_str(CreateTimestamp()), "timestamp"); EXPECT_EQ(to_str(CreateKSym()), "ksym_t"); EXPECT_EQ(to_str(CreateUSym()), "usym_t"); EXPECT_EQ(to_str(CreateUsername()), "username"); EXPECT_EQ(to_str(CreateStackMode()), "stack_mode"); EXPECT_EQ(to_str(CreateTimestampMode()), "timestamp_mode"); EXPECT_EQ(to_str(CreateCgroupPath()), "cgroup_path_t"); EXPECT_EQ(to_str(CreateStrerror()), "strerror_t"); EXPECT_EQ(to_str(CreateHist()), "hist_t"); EXPECT_EQ(to_str(CreateLhist()), "lhist_t"); EXPECT_EQ(to_str(CreateNone()), "none"); EXPECT_EQ(to_str(CreateVoid()), "void"); } } // namespace bpftrace::test::types bpftrace-0.23.2/tests/utils.cpp000066400000000000000000000373631477746507000164270ustar00rootroot00000000000000#include #include #include #include #include "utils.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include #include namespace bpftrace::test::utils { TEST(utils, split_string) { std::vector tokens_empty = {}; std::vector tokens_one_empty = { "" }; std::vector tokens_two_empty = { "", "" }; std::vector tokens_f = { "", "f" }; std::vector tokens_foo_bar = { "foo", "bar" }; std::vector tokens_empty_foo_bar = { "", "foo", "bar" }; std::vector tokens_empty_foo_empty_bar = { "", "foo", "", "bar" }; std::vector tokens_empty_foo_bar_biz = { "", "foo", "bar", "biz" }; EXPECT_EQ(split_string("", '-'), tokens_empty); EXPECT_EQ(split_string("-", '-'), tokens_one_empty); EXPECT_EQ(split_string("--", '-'), tokens_two_empty); EXPECT_EQ(split_string("--", '-', true), tokens_empty); EXPECT_EQ(split_string("-f-", '-'), tokens_f); EXPECT_EQ(split_string("-foo-bar-", '-'), tokens_empty_foo_bar); EXPECT_EQ(split_string("-foo--bar-", '-'), tokens_empty_foo_empty_bar); EXPECT_EQ(split_string("-foo-bar-biz-", '-'), tokens_empty_foo_bar_biz); EXPECT_EQ(split_string("-foo-bar", '-'), tokens_empty_foo_bar); EXPECT_EQ(split_string("foo-bar-", '-'), tokens_foo_bar); EXPECT_EQ(split_string("foo-bar", '-'), tokens_foo_bar); } TEST(utils, split_addrrange_symbol_module) { std::tuple tokens_ar_sym = { "0xffffffff85201511-0xffffffff8520152f", "first_nmi", "" }; std::tuple tokens_ar_sym_mod = { "0xffffffffc17e9373-0xffffffffc17e94ff", "vmx_vmexit", "kvm_intel" }; EXPECT_EQ(split_addrrange_symbol_module( "0xffffffff85201511-0xffffffff8520152f first_nmi"), tokens_ar_sym); EXPECT_EQ(split_addrrange_symbol_module( "0xffffffffc17e9373-0xffffffffc17e94ff vmx_vmexit " "[kvm_intel]"), tokens_ar_sym_mod); } static void test_erase_parameter_list(std::string input, std::string_view expected) { erase_parameter_list(input); EXPECT_EQ(input, expected); } TEST(utils, erase_parameter_list) { // Trivial cases test_erase_parameter_list("", ""); test_erase_parameter_list("()", ""); test_erase_parameter_list("void foo", "void foo"); test_erase_parameter_list("void foo()", "void foo"); test_erase_parameter_list("void foo(Bar &b)", "void foo"); // Qualified functions // we don't need to handle `noexcept` or trailing return type // because they don't appear in the demangled function name test_erase_parameter_list("void foo() &&", "void foo"); test_erase_parameter_list("void foo() const", "void foo"); // Templated parameter/function test_erase_parameter_list("void foo(Bar &b)", "void foo"); test_erase_parameter_list("void foo(Bar &b)", "void foo"); test_erase_parameter_list("void foo()", "void foo"); test_erase_parameter_list("void foo::foo(Bar &b)", "void foo::foo"); // Function pointer test_erase_parameter_list("void foo(void (*func)(int))", "void foo"); test_erase_parameter_list("void foo(void (*func)(int, Bar))", "void foo"); // Missing closing parenthesis test_erase_parameter_list("void foo(Bar &b", "void foo(Bar &b"); } TEST(utils, wildcard_match) { std::vector tokens_not = { "not" }; std::vector tokens_bar = { "bar" }; std::vector tokens_bar_not = { "bar", "not" }; std::vector tokens_foo = { "foo" }; std::vector tokens_biz = { "biz" }; std::vector tokens_foo_biz = { "foo", "biz" }; // start: true, end: true EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, true, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, true, true), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, true, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, true, true), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, true, true), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, true, true), true); // start: false, end: true EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, false, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, false, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, false, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, false, true), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, false, true), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, false, true), true); // start: true, end: false EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, true, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, true, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, true, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, true, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, true, false), true); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, true, false), true); // start: false, end: false EXPECT_EQ(wildcard_match("foobarbiz", tokens_not, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_bar_not, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_biz, false, false), false); EXPECT_EQ(wildcard_match("foobarbiz", tokens_foo_biz, false, false), true); } static void symlink_test_binary(const std::string &destination) { if (symlink("/proc/self/exe", destination.c_str())) { throw std::runtime_error("Couldn't symlink /proc/self/exe to " + destination + ": " + strerror(errno)); } } static std::string get_working_path() { char cwd_path[PATH_MAX]; if (::getcwd(cwd_path, PATH_MAX) == nullptr) { throw std::runtime_error( "getting current working directory for tests failed"); } return std::string(cwd_path); } TEST(utils, resolve_binary_path) { std::string path = "/tmp/bpftrace-test-utils-XXXXXX"; if (::mkdtemp(&path[0]) == nullptr) { throw std::runtime_error("creating temporary path for tests failed"); } // We need real elf executables, linking test binary allows us to do that // without additional dependencies. symlink_test_binary(path + "/executable"); symlink_test_binary(path + "/executable2"); int fd; fd = open((path + "/nonexecutable").c_str(), O_CREAT, S_IRUSR); close(fd); fd = open((path + "/nonexecutable2").c_str(), O_CREAT, S_IRUSR); close(fd); std::vector paths_empty = {}; std::vector paths_one_executable = { path + "/executable" }; std::vector paths_all_executables = { path + "/executable", path + "/executable2" }; EXPECT_EQ(resolve_binary_path(path + "/does/not/exist"), paths_empty); EXPECT_EQ(resolve_binary_path(path + "/does/not/exist*"), paths_empty); EXPECT_EQ(resolve_binary_path(path + "/nonexecutable"), paths_empty); EXPECT_EQ(resolve_binary_path(path + "/nonexecutable*"), paths_empty); EXPECT_EQ(resolve_binary_path(path + "/executable"), paths_one_executable); EXPECT_EQ(resolve_binary_path(path + "/executable*"), paths_all_executables); EXPECT_EQ(resolve_binary_path(path + "/*executable*"), paths_all_executables); EXPECT_GT(std::filesystem::remove_all(path), 0); } TEST(utils, abs_path) { std::string path = "/tmp/bpftrace-test-utils-XXXXXX"; std::string rel_file = "bpftrace-test-utils-abs-path"; if (::mkdtemp(&path[0]) == nullptr) { throw std::runtime_error("creating temporary path for tests failed"); } int fd; fd = open((path + "/somefile").c_str(), O_CREAT, S_IRUSR); close(fd); fd = open(rel_file.c_str(), O_CREAT, S_IRUSR); close(fd); // Translates absolute path with '../..' EXPECT_EQ(abs_path(path + "/../.." + path + "/somefile"), path + "/somefile"); // Translates relative path with './' EXPECT_EQ(abs_path("./" + rel_file), get_working_path() + "/" + rel_file); // /proc//root path returned as is (and doesn't throw) EXPECT_NO_THROW( abs_path(std::string("/proc/1/root/usr/local/bin/usdt_test.so"))); EXPECT_EQ(abs_path(std::string("/proc/1/root/usr/local/bin/usdt_test.so")), std::string("/proc/1/root/usr/local/bin/usdt_test.so")); EXPECT_TRUE(std::filesystem::remove(rel_file)); EXPECT_GT(std::filesystem::remove_all(path), 0); } TEST(utils, get_cgroup_hierarchy_roots) { auto roots = get_cgroup_hierarchy_roots(); // Check that each entry is a proper cgroup filesystem. The first set are // cgroupv1 results, and the second set are cgroupv2 results. for (auto root : roots[0]) { std::filesystem::path root_path(root); EXPECT_TRUE(std::filesystem::exists(root_path / "cgroup.procs")); } for (auto root : roots[1]) { std::filesystem::path root_path(root); 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"); } EXPECT_GT(std::filesystem::remove_all(tmpdir), 0); } TEST(utils, parse_kconfig) { char path[] = "/tmp/configXXXXXX"; int fd = mkstemp(path); const std::string config = "# Intro comment\n" "CONFIG_YES=y\n" "CONFIG_MOD=m\n" "CONFIG_VAL=42\n" "# CONFIG_NO is not set"; EXPECT_EQ(write(fd, config.c_str(), config.length()), config.length()); setenv("BPFTRACE_KCONFIG_TEST", path, true); close(fd); KConfig kconfig; ASSERT_TRUE(kconfig.has_value("CONFIG_YES", "y")); ASSERT_TRUE(kconfig.has_value("CONFIG_MOD", "m")); ASSERT_TRUE(kconfig.has_value("CONFIG_VAL", "42")); ASSERT_EQ(kconfig.config.find("CONFIG_NO"), kconfig.config.end()); unlink(path); } TEST(utils, sanitiseBPFProgramName) { const std::string name = "uprobe:/bin/bash:main+0x30"; const std::string sanitised = sanitise_bpf_program_name(name); ASSERT_EQ(sanitised, "uprobe__bin_bash_main_0x30"); const std::string long_name = "uretprobe:/this/is/a/very/long/path/to/a/binary/executable:" "this_is_a_very_long_function_name_which_exceeds_the_KSYM_NAME_LEN_" "limit_of_BPF_program_name"; const std::string long_sanitised = sanitise_bpf_program_name(long_name); ASSERT_EQ(long_sanitised, "uretprobe__this_is_a_very_long_path_to_a_binary_executable_this_" "is_a_very_long_function_name_which_exceeds_the_ba30ddc67a52bad2"); } // Run a function with environment var set to specific value. // Does its best to clean up env var so it doesn't leak between tests. static void with_env(const std::string &key, const std::string &val, std::function fn) { EXPECT_EQ(::setenv(key.c_str(), val.c_str(), 1), 0); try { fn(); } catch (const std::exception &ex) { EXPECT_EQ(::unsetenv(key.c_str()), 0); throw ex; } EXPECT_EQ(::unsetenv(key.c_str()), 0); } TEST(utils, find_in_path) { std::string tmpdir = "/tmp/bpftrace-test-utils-XXXXXX"; ASSERT_TRUE(::mkdtemp(&tmpdir[0])); // Create some directories const std::filesystem::path path(tmpdir); const std::filesystem::path usr_bin = path / "usr" / "bin"; const std::filesystem::path usr_local_bin = path / "usr" / "local" / "bin"; ASSERT_TRUE(std::filesystem::create_directories(usr_bin)); ASSERT_TRUE(std::filesystem::create_directories(usr_local_bin)); // Create some dummy binaries const std::filesystem::path usr_bin_echo = usr_bin / "echo"; const std::filesystem::path usr_local_bin_echo = usr_local_bin / "echo"; const std::filesystem::path usr_bin_cat = usr_bin / "cat"; { std::ofstream(usr_bin_echo) << "zz"; std::ofstream(usr_local_bin_echo) << "zz"; std::ofstream(usr_bin_cat) << "zz"; } // Test basic find with_env("PATH", usr_bin, [&]() { auto f = find_in_path("echo"); ASSERT_TRUE(f.has_value()); EXPECT_TRUE(f->native().find("/usr/bin/echo") != std::string::npos); }); // Test no entries found with_env("PATH", usr_bin, [&]() { auto f = find_in_path("echoz"); ASSERT_FALSE(f.has_value()); }); // Test precedence in find with two entries in $PATH auto two_path = usr_local_bin.native() + ":" + usr_bin.native(); with_env("PATH", two_path, [&]() { auto f = find_in_path("echo"); ASSERT_TRUE(f.has_value()); EXPECT_TRUE(f->native().find("/usr/local/bin/echo") != std::string::npos); }); // Test no entries found with two entries in $PATH with_env("PATH", two_path, [&]() { auto f = find_in_path("echoz"); ASSERT_FALSE(f.has_value()); }); // Test empty $PATH with_env("PATH", "", [&]() { auto f = find_in_path("echo"); ASSERT_FALSE(f.has_value()); }); // Cleanup EXPECT_TRUE(std::filesystem::remove_all(path)); } // These tests are a bit hacky and rely on repository structure. // // They rely on the fact that the test binary is in the same directory // as some of the other test binaries. // // Hopefully they are easy to maintain. If not, please delete. TEST(utils, find_near_self) { auto runtime_tests = find_near_self("runtime-tests.sh"); // clang-tidy is not aware ASSERT_*() terminates testcase // NOLINTBEGIN(bugprone-unchecked-optional-access) ASSERT_TRUE(runtime_tests.has_value()); EXPECT_TRUE(runtime_tests->filename() == "runtime-tests.sh"); EXPECT_TRUE(std::filesystem::exists(*runtime_tests)); // NOLINTEND(bugprone-unchecked-optional-access) EXPECT_FALSE(find_near_self("SHOULD_NOT_EXIST").has_value()); } TEST(utils, get_pids_for_program) { auto pids = get_pids_for_program("/proc/self/exe"); ASSERT_EQ(pids.size(), 1); ASSERT_EQ(pids[0], getpid()); pids = get_pids_for_program("/doesnotexist"); ASSERT_EQ(pids.size(), 0); } TEST(utils, round_up_to_next_power_of_two) { // 2^31 = 2147483648 which is max power of 2 within uint32_t constexpr uint32_t max_power_of_two = 2147483648; ASSERT_EQ(round_up_to_next_power_of_two(0), 0); ASSERT_EQ(round_up_to_next_power_of_two(1), 1); ASSERT_EQ(round_up_to_next_power_of_two(7), 8); ASSERT_EQ(round_up_to_next_power_of_two(55), 64); ASSERT_EQ(round_up_to_next_power_of_two(128), 128); ASSERT_EQ(round_up_to_next_power_of_two(max_power_of_two - 1), max_power_of_two); ASSERT_EQ(round_up_to_next_power_of_two(max_power_of_two), max_power_of_two); } } // namespace bpftrace::test::utils bpftrace-0.23.2/tools/000077500000000000000000000000001477746507000145455ustar00rootroot00000000000000bpftrace-0.23.2/tools/CMakeLists.txt000066400000000000000000000006641477746507000173130ustar00rootroot00000000000000option(INSTALL_TOOL_DOCS "Install example text files demonstrating tools" ON) if (INSTALL_TOOL_DOCS) file(GLOB TXT_FILES *.txt) list(REMOVE_ITEM TXT_FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) install(FILES ${TXT_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bpftrace/tools/doc) endif() file(GLOB BT_FILES *.bt) install(PROGRAMS ${BT_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bpftrace/tools) add_subdirectory(old) bpftrace-0.23.2/tools/README.md000066400000000000000000000112071477746507000160250ustar00rootroot00000000000000# Tools These tools are a small collection curated by the bpftrace maintainers that have been battle-tested and are packaged with bpftrace. We're currently building a set of [community tools](https://github.com/bpftrace/user-tools), which is now accepting [contributions](https://github.com/bpftrace/user-tools/blob/master/CONTRIBUTING.md). [Read more about how tools get added to this repository](../CONTRIBUTING-TOOLS.md). - tools/[bashreadline.bt](bashreadline.bt) - Print entered bash commands system wide. [Examples](bashreadline_example.txt). - tools/[biolatency.bt](biolatency.bt) - Block I/O latency as a histogram. [Examples](biolatency_example.txt). - tools/[biosnoop.bt](biosnoop.bt) - Block I/O tracing tool, showing per I/O latency. [Examples](biosnoop_example.txt). - tools/[biostacks.bt](biostacks.bt) - Show disk I/O latency with initialization stacks. [Examples](biostacks_example.txt). - tools/[bitesize.bt](bitesize.bt) - Show disk I/O size as a histogram. [Examples](bitesize_example.txt). - tools/[capable.bt](capable.bt) - Trace security capability checks. [Examples](capable_example.txt). - tools/[cpuwalk.bt](cpuwalk.bt) - Sample which CPUs are executing processes. [Examples](cpuwalk_example.txt). - tools/[dcsnoop.bt](dcsnoop.bt) - Trace directory entry cache (dcache) lookups. [Examples](dcsnoop_example.txt). - tools/[execsnoop.bt](execsnoop.bt) - Trace new processes via exec() syscalls. [Examples](execsnoop_example.txt). - tools/[gethostlatency.bt](gethostlatency.bt) - Show latency for getaddrinfo/gethostbyname[2] calls. [Examples](gethostlatency_example.txt). - tools/[killsnoop.bt](killsnoop.bt) - Trace signals issued by the kill() syscall. [Examples](killsnoop_example.txt). - tools/[loads.bt](loads.bt) - Print load averages. [Examples](loads_example.txt). - tools/[mdflush.bt](mdflush.bt) - Trace md flush events. [Examples](mdflush_example.txt). - tools/[naptime.bt](naptime.bt) - Show voluntary sleep calls. [Examples](naptime_example.txt). - tools/[opensnoop.bt](opensnoop.bt) - Trace open() syscalls showing filenames. [Examples](opensnoop_example.txt). - tools/[oomkill.bt](oomkill.bt) - Trace OOM killer. [Examples](oomkill_example.txt). - tools/[pidpersec.bt](pidpersec.bt) - Count new processes (via fork). [Examples](pidpersec_example.txt). - tools/[runqlat.bt](runqlat.bt) - CPU scheduler run queue latency as a histogram. [Examples](runqlat_example.txt). - tools/[runqlen.bt](runqlen.bt) - CPU scheduler run queue length as a histogram. [Examples](runqlen_example.txt). - tools/[setuids.bt](setuids.bt) - Trace the setuid syscalls: privilege escalation. [Examples](setuids_example.txt). - tools/[ssllatency.bt](ssllatency.bt) - Summarize SSL/TLS handshake latency as a histogram. [Examples](ssllatency_example.txt) - tools/[sslsnoop.bt](sslsnoop.bt) - Trace SSL/TLS handshake, showing latency and return value. [Examples](sslsnoop_example.txt) - tools/[statsnoop.bt](statsnoop.bt) - Trace stat() syscalls for general debugging. [Examples](statsnoop_example.txt). - tools/[swapin.bt](swapin.bt) - Show swapins by process. [Examples](swapin_example.txt). - tools/[syncsnoop.bt](syncsnoop.bt) - Trace sync() variety of syscalls. [Examples](syncsnoop_example.txt). - tools/[syscount.bt](syscount.bt) - Count system calls. [Examples](syscount_example.txt). - tools/[tcpaccept.bt](tcpaccept.bt) - Trace TCP passive connections (accept()). [Examples](tcpaccept_example.txt). - tools/[tcpconnect.bt](tcpconnect.bt) - Trace TCP active connections (connect()). [Examples](tcpconnect_example.txt). - tools/[tcpdrop.bt](tcpdrop.bt) - Trace kernel-based TCP packet drops with details. [Examples](tcpdrop_example.txt). - tools/[tcplife.bt](tcplife.bt) - Trace TCP session lifespans with connection details. [Examples](tcplife_example.txt). - tools/[tcpretrans.bt](tcpretrans.bt) - Trace TCP retransmits. [Examples](tcpretrans_example.txt). - tools/[tcpsynbl.bt](tcpsynbl.bt) - Show TCP SYN backlog as a histogram. [Examples](tcpsynbl_example.txt). - tools/[threadsnoop.bt](threadsnoop.bt) - List new thread creation. [Examples](threadsnoop_example.txt). - tools/[undump.bt](undump.bt) - Capture UNIX domain socket packages. [Examples](undump_example.txt). - tools/[vfscount.bt](vfscount.bt) - Count VFS calls. [Examples](vfscount_example.txt). - tools/[vfsstat.bt](vfsstat.bt) - Count some VFS calls, with per-second summaries. [Examples](vfsstat_example.txt). - tools/[writeback.bt](writeback.bt) - Trace file system writeback events with details. [Examples](writeback_example.txt). - tools/[xfsdist.bt](xfsdist.bt) - Summarize XFS operation latency distribution as a histogram. [Examples](xfsdist_example.txt). For more eBPF observability tools, see [bcc tools](https://github.com/iovisor/bcc#tools). bpftrace-0.23.2/tools/bashreadline.bt000077500000000000000000000014231477746507000175200ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * bashreadline Print entered bash commands from all running shells. * For Linux, uses bpftrace and eBPF. * * This works by tracing the readline() function using a uretprobe (uprobes). * * USAGE: bashreadline.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ config = { missing_probes = "ignore" } BEGIN { printf("Tracing bash commands... Hit Ctrl-C to end.\n"); printf("%-9s %-6s %s\n", "TIME", "PID", "COMMAND"); } uretprobe:/bin/bash:readline, uretprobe:libreadline:readline /comm == "bash"/ { time("%H:%M:%S "); printf("%-6d %s\n", pid, str(retval)); } bpftrace-0.23.2/tools/bashreadline_example.txt000066400000000000000000000013221477746507000214400ustar00rootroot00000000000000Demonstrations of bashreadline, the Linux bpftrace/eBPF version. This prints bash commands from all running bash shells on the system. For example: # ./bashreadline.bt Attaching 2 probes... Tracing bash commands... Hit Ctrl-C to end. TIME PID COMMAND 06:40:06 5526 df -h 06:40:09 5526 ls -l 06:40:18 5526 echo hello bpftrace 06:40:42 5526 echooo this is a failed command, but we can see it anyway ^C The entered command may fail. This is just showing what command lines were entered interactively for bash to process. It works by tracing the return of the readline() function using uprobes (specifically a uretprobe). There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.23.2/tools/biolatency-kp.bt000077500000000000000000000016701477746507000176440ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * biolatency.bt Block I/O latency as a histogram. * For Linux, uses bpftrace, eBPF. * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * This version of the tool uses kprobes to attach to kernel events. * Note that these do not exist or are inlined on newer kernels * (since kernel version 6.4) and therefore this version will not work. * On newer kernels, use biolatency.bt. * * 13-Sep-2018 Brendan Gregg Created this. */ config = { missing_probes = "ignore" } BEGIN { printf("Tracing block device I/O... Hit Ctrl-C to end.\n"); } kprobe:blk_account_io_start, kprobe:__blk_account_io_start { @start[arg0] = nsecs; } kprobe:blk_account_io_done, kprobe:__blk_account_io_done /@start[arg0]/ { @usecs = hist((nsecs - @start[arg0]) / 1000); delete(@start, arg0); } END { clear(@start); } bpftrace-0.23.2/tools/biolatency.bt000077500000000000000000000012511477746507000172270ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * biolatency.bt Block I/O latency as a histogram. * For Linux, uses bpftrace, eBPF. * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 13-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing block device I/O... Hit Ctrl-C to end.\n"); } tracepoint:block:block_bio_queue { @start[args.sector] = nsecs; } tracepoint:block:block_rq_complete, tracepoint:block:block_bio_complete /@start[args.sector]/ { @usecs = hist((nsecs - @start[args.sector]) / 1000); delete(@start, args.sector); } END { clear(@start); } bpftrace-0.23.2/tools/biolatency_example.txt000066400000000000000000000033761477746507000211630ustar00rootroot00000000000000Demonstrations of biolatency, the Linux BPF/bpftrace version. This traces block I/O, and shows latency as a power-of-2 histogram. For example: # ./biolatency-kp.bt Attaching 3 probes... Tracing block device I/O... Hit Ctrl-C to end. ^C @usecs: [256, 512) 2 | | [512, 1K) 10 |@ | [1K, 2K) 426 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [2K, 4K) 230 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4K, 8K) 9 |@ | [8K, 16K) 128 |@@@@@@@@@@@@@@@ | [16K, 32K) 68 |@@@@@@@@ | [32K, 64K) 0 | | [64K, 128K) 0 | | [128K, 256K) 10 |@ | While tracing, this shows that 426 block I/O had a latency of between 1K and 2K usecs (1024 and 2048 microseconds), which is between 1 and 2 milliseconds. There are also two modes visible, one between 1 and 2 milliseconds, and another between 8 and 16 milliseconds: this sounds like cache hits and cache misses. There were also 10 I/O with latency 128 to 256 ms: outliers. Other tools and instrumentation, like biosnoop.bt, can shed more light on those outliers. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. "biolatency.bt" is an updated version of "biolatency-kp.bt" and does basically the same thing utilizing the tracepoints instead of kprobes. bpftrace-0.23.2/tools/biosnoop.bt000077500000000000000000000020621477746507000167270ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * biosnoop.bt Block I/O tracing tool, showing per I/O latency. * For Linux, uses bpftrace, eBPF. * * TODO: Add offset and size columns. * * This is a bpftrace version of the bcc tool of the same name. */ BEGIN { printf("%-12s %-14s %-16s %-6s %7s\n", "TIME(ms)", "DISK ID", "COMM", "PID", "LAT(ms)"); printf(" MAJOR MINOR\n") } tracepoint:block:block_io_start { $key = (args.dev, args.sector); @start[$key] = nsecs; @iopid[$key] = pid; @iocomm[$key] = comm; } tracepoint:block:block_io_done /@start[args.dev, args.sector] != 0 && @iopid[args.dev, args.sector] != 0 && @iocomm[args.dev, args.sector] != ""/ { $key = (args.dev, args.sector); $now = nsecs; $major = args.dev >> 20; $minor = args.dev & ((1 << 20) - 1); printf("%-12u %-5d %-8d %-16s %-6d %7d\n", elapsed / 1e6, $major, $minor, @iocomm[$key], @iopid[$key], ($now - @start[$key]) / 1e6); delete(@start, $key); delete(@iopid, $key); delete(@iocomm, $key); } END { clear(@start); clear(@iopid); clear(@iocomm); } bpftrace-0.23.2/tools/biosnoop_example.txt000066400000000000000000000040161477746507000206520ustar00rootroot00000000000000Demonstrations of biosnoop, the Linux BPF/bpftrace version. This traces block I/O, and shows the issuing process (at least, the process that was on-CPU at the time of queue insert) and the latency of the I/O: # ./biosnoop.bt Attaching 4 probes... TIME(ms) DISK ID COMM PID LAT(ms) MAJOR MINOR 611 259 0 bash 4179 10 611 259 0 cksum 4179 0 627 259 0 cksum 4179 15 641 259 0 cksum 4179 13 644 259 0 cksum 4179 3 658 259 0 cksum 4179 13 673 259 0 cksum 4179 14 686 259 0 cksum 4179 13 701 259 0 cksum 4179 14 710 259 0 cksum 4179 8 717 259 0 cksum 4179 6 728 259 0 cksum 4179 10 735 259 0 cksum 4179 6 751 259 0 cksum 4179 10 758 259 0 cksum 4179 17 783 259 0 cksum 4179 12 796 259 0 cksum 4179 25 802 259 0 cksum 4179 32 [...] This output shows the cksum process was issuing block I/O, which were completing with around 12 milliseconds of latency. Each block I/O event is printed out, with a completion time as the first column, measured from program start. An example of some background flushing: # ./biosnoop.bt Attaching 4 probes... TIME(ms) DISK ID COMM PID LAT(ms) MAJOR MINOR 2966 259 0 jbd2/nvme0n1-8 615 0 2967 259 0 jbd2/nvme0n1-8 615 0 [...] There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides more fields. bpftrace-0.23.2/tools/biostacks.bt000077500000000000000000000016041477746507000170620ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * biostacks - Show disk I/O latency with initialization stacks. * * See BPF Performance Tools, Chapter 9, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. */ BEGIN { printf("Tracing block I/O with init stacks. Hit Ctrl-C to end.\n"); } tracepoint:block:block_io_start { $key = (args.dev, args.sector); @reqstack[$key] = kstack; @reqts[$key] = nsecs; } tracepoint:block:block_rq_issue /@reqts[args.dev, args.sector]/ { $key = (args.dev, args.sector); @usecs[@reqstack[$key]] = hist(nsecs - @reqts[$key]); delete(@reqstack, $key); delete(@reqts, $key); } END { clear(@reqstack); clear(@reqts); } bpftrace-0.23.2/tools/biostacks_example.txt000066400000000000000000000035671477746507000210160ustar00rootroot00000000000000Demonstrations of biostacks, the Linux BCC/eBPF version. This tool shows block I/O latency as a histogram, with the kernel stack trace that initiated the I/O. This can help explain disk I/O that is not directly requested by applications (eg, metadata reads on writes, resilvering, etc). For example: # ./biostacks.bt Attaching 5 probes... Tracing block I/O with init stacks. Hit Ctrl-C to end. ^C @usecs[ blk_account_io_start+1 blk_mq_make_request+1102 generic_make_request+292 submit_bio+115 _xfs_buf_ioapply+798 xfs_buf_submit+101 xlog_bdstrat+43 xlog_sync+705 xlog_state_release_iclog+108 _xfs_log_force+542 xfs_log_force+44 xfsaild+428 kthread+289 ret_from_fork+53 ]: [64K, 128K) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [...] @usecs[ blk_account_io_start+1 blk_mq_make_request+707 generic_make_request+292 submit_bio+115 xfs_add_to_ioend+455 xfs_do_writepage+758 write_cache_pages+524 xfs_vm_writepages+190 do_writepages+75 __writeback_single_inode+69 writeback_sb_inodes+481 __writeback_inodes_wb+103 wb_writeback+625 wb_workfn+384 process_one_work+478 worker_thread+50 kthread+289 ret_from_fork+53 ]: [8K, 16K) 560 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [16K, 32K) 218 |@@@@@@@@@@@@@@@@@@@@ | [32K, 64K) 26 |@@ | [64K, 128K) 2 | | [128K, 256K) 53 |@@@@ | [256K, 512K) 60 |@@@@@ | This output shows the most frequent stack was XFS writeback, with latencies between 8 and 512 microseconds. The other stack included here shows an XFS log sync. bpftrace-0.23.2/tools/bitesize.bt000077500000000000000000000010671477746507000167210ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * bitesize Show disk I/O size as a histogram. * For Linux, uses bpftrace and eBPF. * * USAGE: bitesize.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 07-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing block device I/O... Hit Ctrl-C to end.\n"); } tracepoint:block:block_rq_issue { @[args.comm] = hist(args.bytes); } END { printf("\nI/O size (bytes) histograms by process name:"); } bpftrace-0.23.2/tools/bitesize_example.txt000066400000000000000000000056731477746507000206520ustar00rootroot00000000000000Demonstrations of bitesize, the Linux bpftrace/eBPF version. This traces disk I/O via the block I/O interface, and prints a summary of I/O sizes as histograms for each process name. For example: # ./bitesize.bt Attaching 3 probes... Tracing block device I/O... Hit Ctrl-C to end. ^C I/O size (bytes) histograms by process name: @[cleanup]: [4K, 8K) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @[postdrop]: [4K, 8K) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @[jps]: [4K, 8K) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [8K, 16K) 0 | | [16K, 32K) 0 | | [32K, 64K) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @[kworker/2:1H]: [0] 3 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [1] 0 | | [2, 4) 0 | | [4, 8) 0 | | [8, 16) 0 | | [16, 32) 0 | | [32, 64) 0 | | [64, 128) 0 | | [128, 256) 0 | | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 0 | | [4K, 8K) 0 | | [8K, 16K) 0 | | [16K, 32K) 0 | | [32K, 64K) 0 | | [64K, 128K) 1 |@@@@@@@@@@@@@@@@@ | @[jbd2/nvme0n1-8]: [4K, 8K) 3 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8K, 16K) 0 | | [16K, 32K) 0 | | [32K, 64K) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [64K, 128K) 1 |@@@@@@@@@@@@@@@@@ | @[dd]: [16K, 32K) 921 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| The most active process while tracing was "dd", which issues 921 I/O between 16 Kbytes and 32 Kbytes in size. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.23.2/tools/capable.bt000077500000000000000000000036061477746507000164730ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * capable Trace security capability checks (cap_capable()). * For Linux, uses bpftrace and eBPF. * * USAGE: capable.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing cap_capable syscalls... Hit Ctrl-C to end.\n"); printf("%-9s %-6s %-6s %-16s %-4s %-20s AUDIT\n", "TIME", "UID", "PID", "COMM", "CAP", "NAME"); @cap[0] = "CAP_CHOWN"; @cap[1] = "CAP_DAC_OVERRIDE"; @cap[2] = "CAP_DAC_READ_SEARCH"; @cap[3] = "CAP_FOWNER"; @cap[4] = "CAP_FSETID"; @cap[5] = "CAP_KILL"; @cap[6] = "CAP_SETGID"; @cap[7] = "CAP_SETUID"; @cap[8] = "CAP_SETPCAP"; @cap[9] = "CAP_LINUX_IMMUTABLE"; @cap[10] = "CAP_NET_BIND_SERVICE"; @cap[11] = "CAP_NET_BROADCAST"; @cap[12] = "CAP_NET_ADMIN"; @cap[13] = "CAP_NET_RAW"; @cap[14] = "CAP_IPC_LOCK"; @cap[15] = "CAP_IPC_OWNER"; @cap[16] = "CAP_SYS_MODULE"; @cap[17] = "CAP_SYS_RAWIO"; @cap[18] = "CAP_SYS_CHROOT"; @cap[19] = "CAP_SYS_PTRACE"; @cap[20] = "CAP_SYS_PACCT"; @cap[21] = "CAP_SYS_ADMIN"; @cap[22] = "CAP_SYS_BOOT"; @cap[23] = "CAP_SYS_NICE"; @cap[24] = "CAP_SYS_RESOURCE"; @cap[25] = "CAP_SYS_TIME"; @cap[26] = "CAP_SYS_TTY_CONFIG"; @cap[27] = "CAP_MKNOD"; @cap[28] = "CAP_LEASE"; @cap[29] = "CAP_AUDIT_WRITE"; @cap[30] = "CAP_AUDIT_CONTROL"; @cap[31] = "CAP_SETFCAP"; @cap[32] = "CAP_MAC_OVERRIDE"; @cap[33] = "CAP_MAC_ADMIN"; @cap[34] = "CAP_SYSLOG"; @cap[35] = "CAP_WAKE_ALARM"; @cap[36] = "CAP_BLOCK_SUSPEND"; @cap[37] = "CAP_AUDIT_READ"; @cap[38] = "CAP_PERFMON"; @cap[39] = "CAP_BPF"; @cap[40] = "CAP_CHECKPOINT_RESTORE"; } kprobe:cap_capable { $cap = arg2; $audit = arg3; time("%H:%M:%S "); printf("%-6d %-6d %-16s %-4d %-20s %d\n", uid, pid, comm, $cap, @cap[$cap], $audit); } END { clear(@cap); } bpftrace-0.23.2/tools/capable_example.txt000066400000000000000000000051431477746507000204130ustar00rootroot00000000000000Demonstrations of capable, the Linux bpftrace/eBPF version. capable traces calls to the kernel cap_capable() function, which does security capability checks, and prints details for each call. For example: # ./capable.bt TIME UID PID COMM CAP NAME AUDIT 22:11:23 114 2676 snmpd 12 CAP_NET_ADMIN 1 22:11:23 0 6990 run 24 CAP_SYS_RESOURCE 1 22:11:23 0 7003 chmod 3 CAP_FOWNER 1 22:11:23 0 7003 chmod 4 CAP_FSETID 1 22:11:23 0 7005 chmod 4 CAP_FSETID 1 22:11:23 0 7005 chmod 4 CAP_FSETID 1 22:11:23 0 7006 chown 4 CAP_FSETID 1 22:11:23 0 7006 chown 4 CAP_FSETID 1 22:11:23 0 6990 setuidgid 6 CAP_SETGID 1 22:11:23 0 6990 setuidgid 6 CAP_SETGID 1 22:11:23 0 6990 setuidgid 7 CAP_SETUID 1 22:11:24 0 7013 run 24 CAP_SYS_RESOURCE 1 22:11:24 0 7026 chmod 3 CAP_FOWNER 1 22:11:24 0 7026 chmod 4 CAP_FSETID 1 22:11:24 0 7028 chmod 4 CAP_FSETID 1 22:11:24 0 7028 chmod 4 CAP_FSETID 1 22:11:24 0 7029 chown 4 CAP_FSETID 1 22:11:24 0 7029 chown 4 CAP_FSETID 1 22:11:24 0 7013 setuidgid 6 CAP_SETGID 1 22:11:24 0 7013 setuidgid 6 CAP_SETGID 1 22:11:24 0 7013 setuidgid 7 CAP_SETUID 1 22:11:25 0 7036 run 24 CAP_SYS_RESOURCE 1 22:11:25 0 7049 chmod 3 CAP_FOWNER 1 22:11:25 0 7049 chmod 4 CAP_FSETID 1 22:11:25 0 7051 chmod 4 CAP_FSETID 1 22:11:25 0 7051 chmod 4 CAP_FSETID 1 [...] This can be useful for general debugging, and also security enforcement: determining a whitelist of capabilities an application needs. The output above includes various capability checks: snmpd checking CAP_NET_ADMIN, run checking CAP_SYS_RESOURCES, then some short-lived processes checking CAP_FOWNER, CAP_FSETID, etc. To see what each of these capabilities does, check the capabilities(7) man page and the kernel source. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.23.2/tools/cpuwalk.bt000077500000000000000000000007611477746507000165510ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * cpuwalk Sample which CPUs are executing processes. * For Linux, uses bpftrace and eBPF. * * USAGE: cpuwalk.bt * * This is a bpftrace version of the DTraceToolkit tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Sampling CPU at 99hz... Hit Ctrl-C to end.\n"); } profile:hz:99 /pid/ { @cpu = lhist(cpu, 0, 1000, 1); } bpftrace-0.23.2/tools/cpuwalk_example.txt000066400000000000000000000114671477746507000205000ustar00rootroot00000000000000Demonstrations of cpuwalk, the Linux bpftrace/eBPF version. cpuwalk samples which CPUs processes are running on, and prints a summary histogram. For example, here is a Linux kernel build on a 36-CPU server: # ./cpuwalk.bt Attaching 2 probes... Sampling CPU at 99hz... Hit Ctrl-C to end. ^C @cpu: [0, 1) 130 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [1, 2) 137 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [2, 3) 99 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [3, 4) 99 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [4, 5) 82 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [5, 6) 34 |@@@@@@@@@@@@ | [6, 7) 67 |@@@@@@@@@@@@@@@@@@@@@@@@ | [7, 8) 41 |@@@@@@@@@@@@@@@ | [8, 9) 97 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [9, 10) 140 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [10, 11) 105 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [11, 12) 77 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [12, 13) 39 |@@@@@@@@@@@@@@ | [13, 14) 58 |@@@@@@@@@@@@@@@@@@@@@ | [14, 15) 64 |@@@@@@@@@@@@@@@@@@@@@@@ | [15, 16) 57 |@@@@@@@@@@@@@@@@@@@@@ | [16, 17) 99 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [17, 18) 56 |@@@@@@@@@@@@@@@@@@@@ | [18, 19) 44 |@@@@@@@@@@@@@@@@ | [19, 20) 80 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [20, 21) 64 |@@@@@@@@@@@@@@@@@@@@@@@ | [21, 22) 59 |@@@@@@@@@@@@@@@@@@@@@ | [22, 23) 88 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [23, 24) 84 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [24, 25) 29 |@@@@@@@@@@ | [25, 26) 48 |@@@@@@@@@@@@@@@@@ | [26, 27) 62 |@@@@@@@@@@@@@@@@@@@@@@@ | [27, 28) 66 |@@@@@@@@@@@@@@@@@@@@@@@@ | [28, 29) 57 |@@@@@@@@@@@@@@@@@@@@@ | [29, 30) 59 |@@@@@@@@@@@@@@@@@@@@@ | [30, 31) 56 |@@@@@@@@@@@@@@@@@@@@ | [31, 32) 23 |@@@@@@@@ | [32, 33) 90 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [33, 34) 62 |@@@@@@@@@@@@@@@@@@@@@@@ | [34, 35) 39 |@@@@@@@@@@@@@@ | [35, 36) 68 |@@@@@@@@@@@@@@@@@@@@@@@@@ | This shows that all 36 CPUs were active, with some busier than others. Compare that output to the following workload from an application: # ./cpuwalk.bt Attaching 2 probes... Sampling CPU at 99hz... Hit Ctrl-C to end. ^C @cpu: [6, 7) 243 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [7, 8) 0 | | [8, 9) 0 | | [9, 10) 0 | | [10, 11) 0 | | [11, 12) 0 | | [12, 13) 0 | | [13, 14) 0 | | [14, 15) 0 | | [15, 16) 0 | | [16, 17) 0 | | [17, 18) 0 | | [18, 19) 0 | | [19, 20) 0 | | [20, 21) 1 | | In this case, only a single CPU (6) is really active doing work. Only a single sample was taken of another CPU (20) running a process. If the workload was supposed to be making use of multiple CPUs, it isn't, and that can be investigated (application's configuration, number of threads, CPU binding, etc). bpftrace-0.23.2/tools/dcsnoop.bt000077500000000000000000000023551477746507000165510ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * dcsnoop Trace directory entry cache (dcache) lookups. * For Linux, uses bpftrace and eBPF. * * This uses kernel dynamic tracing of kernel functions, lookup_fast() and * d_lookup(), which will need to be modified to match kernel changes. See * code comments. * * USAGE: dcsnoop.bt * * 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 // from fs/namei.c: struct nameidata { struct path path; struct qstr last; // [...] }; #endif BEGIN { printf("Tracing dcache lookups... Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-16s %1s %s\n", "TIME", "PID", "COMM", "T", "FILE"); } // comment out this block to avoid showing hits: kprobe:lookup_fast, kprobe:lookup_fast.constprop.* { $nd = (struct nameidata *)arg0; printf("%-8d %-6d %-16s R %s\n", elapsed / 1e6, pid, comm, str($nd->last.name)); } kprobe:d_lookup { $name = (struct qstr *)arg1; @fname[tid] = $name->name; } kretprobe:d_lookup /@fname[tid]/ { printf("%-8d %-6d %-16s M %s\n", elapsed / 1e6, pid, comm, str(@fname[tid])); delete(@fname, tid); } bpftrace-0.23.2/tools/dcsnoop_example.txt000066400000000000000000000110041477746507000204620ustar00rootroot00000000000000Demonstrations of dcsnoop, the Linux bpftrace/eBPF version. dcsnoop traces directory entry cache (dcache) lookups, and can be used for further investigation beyond dcstat(8). The output is likely verbose, as dcache lookups are likely frequent. For example: # ./dcsnoop.bt Attaching 4 probes... Tracing dcache lookups... Hit Ctrl-C to end. TIME PID COMM T FILE 427 1518 irqbalance R proc/interrupts 427 1518 irqbalance R interrupts 427 1518 irqbalance R proc/stat 427 1518 irqbalance R stat 483 2440 snmp-pass R proc/cpuinfo 483 2440 snmp-pass R cpuinfo 486 2440 snmp-pass R proc/stat 486 2440 snmp-pass R stat 834 1744 snmpd R proc/net/dev 834 1744 snmpd R net/dev 834 1744 snmpd R self/net 834 1744 snmpd R 1744 834 1744 snmpd R net 834 1744 snmpd R dev 834 1744 snmpd R proc/net/if_inet6 834 1744 snmpd R net/if_inet6 834 1744 snmpd R self/net 834 1744 snmpd R 1744 834 1744 snmpd R net 834 1744 snmpd R if_inet6 835 1744 snmpd R sys/class/net/docker0/device/vendor 835 1744 snmpd R class/net/docker0/device/vendor 835 1744 snmpd R net/docker0/device/vendor 835 1744 snmpd R docker0/device/vendor 835 1744 snmpd R devices/virtual/net/docker0 835 1744 snmpd R virtual/net/docker0 835 1744 snmpd R net/docker0 835 1744 snmpd R docker0 835 1744 snmpd R device/vendor 835 1744 snmpd R proc/sys/net/ipv4/neigh/docker0/retrans_time_ms 835 1744 snmpd R sys/net/ipv4/neigh/docker0/retrans_time_ms 835 1744 snmpd R net/ipv4/neigh/docker0/retrans_time_ms 835 1744 snmpd R ipv4/neigh/docker0/retrans_time_ms 835 1744 snmpd R neigh/docker0/retrans_time_ms 835 1744 snmpd R docker0/retrans_time_ms 835 1744 snmpd R retrans_time_ms 835 1744 snmpd R proc/sys/net/ipv6/neigh/docker0/retrans_time_ms 835 1744 snmpd R sys/net/ipv6/neigh/docker0/retrans_time_ms 835 1744 snmpd R net/ipv6/neigh/docker0/retrans_time_ms 835 1744 snmpd R ipv6/neigh/docker0/retrans_time_ms 835 1744 snmpd R neigh/docker0/retrans_time_ms 835 1744 snmpd R docker0/retrans_time_ms 835 1744 snmpd R retrans_time_ms 835 1744 snmpd R proc/sys/net/ipv6/conf/docker0/forwarding 835 1744 snmpd R sys/net/ipv6/conf/docker0/forwarding 835 1744 snmpd R net/ipv6/conf/docker0/forwarding 835 1744 snmpd R ipv6/conf/docker0/forwarding 835 1744 snmpd R conf/docker0/forwarding [...] 5154 934 cksum R usr/bin/basename 5154 934 cksum R bin/basename 5154 934 cksum R basename 5154 934 cksum R usr/bin/bashbug 5154 934 cksum R bin/bashbug 5154 934 cksum R bashbug 5154 934 cksum M bashbug 5155 934 cksum R usr/bin/batch 5155 934 cksum R bin/batch 5155 934 cksum R batch 5155 934 cksum M batch 5155 934 cksum R usr/bin/bc 5155 934 cksum R bin/bc 5155 934 cksum R bc 5155 934 cksum M bc 5169 934 cksum R usr/bin/bdftopcf 5169 934 cksum R bin/bdftopcf 5169 934 cksum R bdftopcf 5169 934 cksum M bdftopcf 5173 934 cksum R usr/bin/bdftruncate 5173 934 cksum R bin/bdftruncate 5173 934 cksum R bdftruncate 5173 934 cksum M bdftruncate The way the dcache is currently implemented, each component of a path is checked in turn. The first line, showing "proc/interrupts" from irqbalance, will be a lookup for "proc" in a directory (that isn't shown here). If it finds "proc", it will then lookup "interrupts" inside net. The script is easily modifiable to only show misses, reducing the volume of the output. Or use the bcc version of this tool, which only shows misses by default: https://github.com/iovisor/bcc bpftrace-0.23.2/tools/execsnoop.bt000077500000000000000000000016401477746507000171030ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * execsnoop.bt Trace new processes via exec() syscalls. * For Linux, uses bpftrace and eBPF. * * This traces when processes call exec(). It is handy for identifying new * processes created via the usual fork()->exec() sequence. Note that the * return value is not currently traced, so the exec() may have failed. * * TODO: switch to tracepoints args. Support more args. Include retval. * * This is a bpftrace version of the bcc tool of the same name. * * 15-Nov-2017 Brendan Gregg Created this. * 11-Sep-2018 " " Switched to use join(). */ #ifndef BPFTRACE_HAVE_BTF #include #endif BEGIN { printf("%-15s %-7s %-7s %s\n", "TIME", "PID", "PPID", "ARGS"); } tracepoint:syscalls:sys_enter_exec* { $task = (struct task_struct *)curtask; printf("%15s %-7d %-7d ", strftime("%H:%M:%S.%f", nsecs), pid, $task->real_parent->pid); join(args.argv); } bpftrace-0.23.2/tools/execsnoop_example.txt000066400000000000000000000027771477746507000210410ustar00rootroot00000000000000Demonstrations of execsnoop, the Linux BPF/bpftrace version. Tracing all new process execution (via exec()): # ./execsnoop.bt Attaching 3 probes... TIME PID PPID ARGS 08:57:52.430193 3187374 1971701 ls --color --color=auto -lh execsnoop.bt execsnoop.bt.0 execsnoop.bt.1 08:57:52.441868 3187378 3187375 man ls 08:57:52.473565 3187384 3187378 preconv -e UTF-8 08:57:52.473620 3187384 3187378 preconv -e UTF-8 08:57:52.473658 3187384 3187378 preconv -e UTF-8 08:57:52.473839 3187385 3187378 tbl 08:57:52.473897 3187385 3187378 tbl 08:57:52.473944 3187385 3187378 tbl 08:57:52.474055 3187386 3187378 nroff -mandoc -Tutf8 08:57:52.474107 3187386 3187378 nroff -mandoc -Tutf8 08:57:52.474145 3187386 3187378 nroff -mandoc -Tutf8 08:57:52.474684 3187388 3187378 less 08:57:52.474739 3187388 3187378 less 08:57:52.474780 3187388 3187378 less 08:57:52.475502 3187389 3187386 groff -Tutf8 -mtty-char -mandoc 08:57:52.476717 3187390 3187389 troff -mtty-char -mandoc -Tutf8 08:57:52.476811 3187391 3187389 grotty The output begins by showing an "ls" command, and then the process execution to serve "man ls". The same exec arguments appear multiple times: in this case they are failing as the $PATH variable is walked, until one finally succeeds. This tool can be used to discover unwanted short-lived processes that may be causing performance issues such as latency perturbations. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides more fields and command line options. bpftrace-0.23.2/tools/gethostlatency.bt000077500000000000000000000023561477746507000201420ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * gethostlatency Trace getaddrinfo/gethostbyname[2] calls. * For Linux, uses bpftrace and eBPF. * * This can be useful for identifying DNS latency, by identifying which * remote host name lookups were slow, and by how much. * * This uses dynamic tracing of user-level functions and registers, and may # need modifications to match your software and processor architecture. * * USAGE: gethostlatency.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ config = { missing_probes = "ignore" } BEGIN { printf("Tracing getaddr/gethost calls... Hit Ctrl-C to end.\n"); printf("%-9s %-6s %-16s %6s %s\n", "TIME", "PID", "COMM", "LATms", "HOST"); } uprobe:libc:getaddrinfo, uprobe:libc:gethostbyname, uprobe:libc:gethostbyname2 { @start[tid] = nsecs; @name[tid] = arg0; } uretprobe:libc:getaddrinfo, uretprobe:libc:gethostbyname, uretprobe:libc:gethostbyname2 /@start[tid]/ { $latms = (nsecs - @start[tid]) / 1e6; time("%H:%M:%S "); printf("%-6d %-16s %6d %s\n", pid, comm, $latms, str(@name[tid])); delete(@start, tid); delete(@name, tid); } bpftrace-0.23.2/tools/gethostlatency_example.txt000066400000000000000000000016331477746507000220610ustar00rootroot00000000000000Demonstrations of gethostlatency, the Linux bpftrace/eBPF version. This traces host name lookup calls (getaddrinfo(), gethostbyname(), and gethostbyname2()), and shows the PID and command performing the lookup, the latency (duration) of the call in milliseconds, and the host string: # ./gethostlatency.bt Attaching 7 probes... Tracing getaddr/gethost calls... Hit Ctrl-C to end. TIME PID COMM LATms HOST 02:52:05 19105 curl 81 www.netflix.com 02:52:12 19111 curl 17 www.netflix.com 02:52:19 19116 curl 9 www.facebook.com 02:52:23 19118 curl 3 www.facebook.com In this example, the first call to lookup "www.netflix.com" took 81 ms, and the second took 17 ms (sounds like some caching). There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.23.2/tools/killsnoop.bt000077500000000000000000000015511477746507000171130ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * killsnoop Trace signals issued by the kill() syscall. * For Linux, uses bpftrace and eBPF. * * USAGE: killsnoop.bt * * Also a basic example of bpftrace. * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 07-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing kill() signals... Hit Ctrl-C to end.\n"); printf("%-15s %7s %-16s %4s %6s %s\n", "TIME", "PID", "COMM", "SIG", "TPID", "RESULT"); } tracepoint:syscalls:sys_enter_kill { @tpid[tid] = args.pid; @tsig[tid] = args.sig; } tracepoint:syscalls:sys_exit_kill /@tpid[tid]/ { printf("%-15s %7d %-16s %4d %6d %6d\n", strftime("%H:%M:%S.%f", nsecs), pid, comm, @tsig[tid], @tpid[tid], args.ret); delete(@tpid, tid); delete(@tsig, tid); } bpftrace-0.23.2/tools/killsnoop_example.txt000066400000000000000000000015161477746507000210360ustar00rootroot00000000000000Demonstrations of killsnoop, the Linux bpftrace/eBPF version. This traces signals sent via the kill() syscall. For example: # ./killsnoop.bt Attaching 3 probes... Tracing kill() signals... Hit Ctrl-C to end. TIME PID COMM SIG TPID RESULT 00:09:37.345938 22485 bash 2 23856 0 00:09:40.838452 22485 bash 2 23856 -3 00:09:31.437104 22485 bash 15 23814 -3 The first line showed a SIGINT (2) sent from PID 22485 (a bash shell) to PID 23856. The result, 0, means success. The next line shows the same signal sent, which resulted in -3, a failure (likely because the target process no longer existed). There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides command line options to customize the output. bpftrace-0.23.2/tools/loads.bt000077500000000000000000000021471477746507000162050ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * loads Prints load averages. * For Linux, uses bpftrace and eBPF. * * These are the same load averages printed by "uptime", but to three decimal * places instead of two (not that it really matters). This is really a * demonstration of fetching and processing a kernel structure from bpftrace. * * USAGE: loads.bt * * This is a bpftrace version of a DTraceToolkit tool. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 10-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Reading load averages... Hit Ctrl-C to end.\n"); } interval:s:1 { /* * See fs/proc/loadavg.c and include/linux/sched/loadavg.h for the * following calculations. */ $avenrun = kaddr("avenrun"); $load1 = *$avenrun; $load5 = *($avenrun + 8); $load15 = *($avenrun + 16); time("%H:%M:%S "); printf("load averages: %d.%03d %d.%03d %d.%03d\n", ($load1 >> 11), (($load1 & ((1 << 11) - 1)) * 1000) >> 11, ($load5 >> 11), (($load5 & ((1 << 11) - 1)) * 1000) >> 11, ($load15 >> 11), (($load15 & ((1 << 11) - 1)) * 1000) >> 11 ); } bpftrace-0.23.2/tools/loads_example.txt000066400000000000000000000015401477746507000201230ustar00rootroot00000000000000Demonstrations of loads, the Linux bpftrace/eBPF version. This is a simple tool that prints the system load averages, to three decimal places each (not that it really matters), as a demonstration of fetching kernel structures from bpftrace: # ./loads.bt Attaching 2 probes... Reading load averages... Hit Ctrl-C to end. 21:29:17 load averages: 2.091 2.048 1.947 21:29:18 load averages: 2.091 2.048 1.947 21:29:19 load averages: 2.091 2.048 1.947 21:29:20 load averages: 2.091 2.048 1.947 21:29:21 load averages: 2.164 2.064 1.953 21:29:22 load averages: 2.164 2.064 1.953 21:29:23 load averages: 2.164 2.064 1.953 ^C These are the same load averages printed by uptime: # uptime 21:29:24 up 2 days, 18:57, 3 users, load average: 2.16, 2.06, 1.95 For more on load averages, see my post: http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html bpftrace-0.23.2/tools/mdflush.bt000077500000000000000000000014071477746507000165430ustar00rootroot00000000000000#!/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.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 { printf("Tracing md flush events... Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-16s %s\n", "TIME", "PID", "COMM", "DEVICE"); } kprobe:md_flush_request { time("%H:%M:%S "); printf("%-6d %-16s %s\n", pid, comm, ((struct bio *)arg1)->bi_bdev->bd_disk->disk_name); } bpftrace-0.23.2/tools/mdflush_example.txt000066400000000000000000000035121477746507000204640ustar00rootroot00000000000000Demonstrations of mdflush, the Linux bpftrace/eBPF version. The mdflush tool traces flushes at the md driver level, and prints details including the time of the flush: # ./mdflush.bt Tracing md flush requests... Hit Ctrl-C to end. TIME PID COMM DEVICE 03:13:49 16770 sync md0 03:14:08 16864 sync md0 03:14:49 496 kworker/1:0H md0 03:14:49 488 xfsaild/md0 md0 03:14:54 488 xfsaild/md0 md0 03:15:00 488 xfsaild/md0 md0 03:15:02 85 kswapd0 md0 03:15:02 488 xfsaild/md0 md0 03:15:05 488 xfsaild/md0 md0 03:15:08 488 xfsaild/md0 md0 03:15:10 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:11 488 xfsaild/md0 md0 03:15:12 488 xfsaild/md0 md0 03:15:13 488 xfsaild/md0 md0 03:15:15 488 xfsaild/md0 md0 03:15:19 496 kworker/1:0H md0 03:15:49 496 kworker/1:0H md0 03:15:55 18840 sync md0 03:16:49 496 kworker/1:0H md0 03:17:19 496 kworker/1:0H md0 03:20:19 496 kworker/1:0H md0 03:21:19 496 kworker/1:0H md0 03:21:49 496 kworker/1:0H md0 03:25:19 496 kworker/1:0H md0 [...] This can be useful for correlation with latency outliers or spikes in disk latency, as measured using another tool (eg, system monitoring). If spikes in disk latency often coincide with md flush events, then it would make flushing a target for tuning. Note that the flush events are likely to originate from higher in the I/O stack, such as from file systems. This traces md processing them, and the timestamp corresponds with when md began to issue the flush to disks. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.23.2/tools/naptime.bt000077500000000000000000000020131477746507000165300ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * naptime - Show voluntary sleep calls. * * See BPF Performance Tools, Chapter 13, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 16-Feb-2019 Brendan Gregg Created this. */ #ifndef BPFTRACE_HAVE_BTF #include #include #endif BEGIN { printf("Tracing sleeps. Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-16s %-6s %-16s %s\n", "TIME", "PPID", "PCOMM", "PID", "COMM", "SECONDS"); } tracepoint:syscalls:sys_enter_nanosleep /args.rqtp->tv_sec + args.rqtp->tv_nsec/ { $task = (struct task_struct *)curtask; time("%H:%M:%S "); printf("%-6d %-16s %-6d %-16s %d.%03d\n", $task->real_parent->pid, $task->real_parent->comm, pid, comm, args.rqtp->tv_sec, (uint64)args.rqtp->tv_nsec / 1e6); } bpftrace-0.23.2/tools/naptime_example.txt000066400000000000000000000015141477746507000204570ustar00rootroot00000000000000Demonstrations of naptime, the Linux bpftrace/eBPF version. Tracing application sleeps via the nanosleep(2) syscall: # ./naptime.bt Attaching 2 probes... Tracing sleeps. Hit Ctrl-C to end. TIME PCOMM PPID COMM PID SECONDS 15:50:00 1 systemd 1319 mysqld 1.000 15:50:01 4388 bash 25250 sleep 5.000 15:50:01 1 systemd 1319 mysqld 1.000 15:50:01 1 systemd 1180 cron 60.000 15:50:01 1 systemd 1180 cron 60.000 15:50:02 1 systemd 1319 mysqld 1.000 [...] The output shows mysqld performing a one second sleep every second (likely a daemon thread), a sleep(1) command sleeping for five seconds and called by bash, and cron threads sleeping for 60 seconds. bpftrace-0.23.2/tools/old/000077500000000000000000000000001477746507000153235ustar00rootroot00000000000000bpftrace-0.23.2/tools/old/CMakeLists.txt000066400000000000000000000001601477746507000200600ustar00rootroot00000000000000file(GLOB BT_FILES *.bt) install(FILES ${BT_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bpftrace/tools/old) bpftrace-0.23.2/tools/old/biosnoop-pre-5.17.bt000077500000000000000000000022251477746507000206620ustar00rootroot00000000000000#!/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); } bpftrace-0.23.2/tools/old/biosnoop-pre-6.4.bt000077500000000000000000000022241477746507000205760ustar00rootroot00000000000000#!/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 <= 6.3. * * 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)->q->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); } bpftrace-0.23.2/tools/old/biostacks.bt000077500000000000000000000017231477746507000176420ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * biostacks - Show disk I/O latency with initialization stacks. * * See BPF Performance Tools, Chapter 9, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * For Linux <= 6.4. * * 19-Mar-2019 Brendan Gregg Created this. */ config = { missing_probes = "ignore" } BEGIN { printf("Tracing block I/O with init stacks. Hit Ctrl-C to end.\n"); } kprobe:blk_account_io_start, kprobe:__blk_account_io_start { @reqstack[arg0] = kstack; @reqts[arg0] = nsecs; } kprobe:blk_start_request, kprobe:blk_mq_start_request /@reqts[arg0]/ { @usecs[@reqstack[arg0]] = hist(nsecs - @reqts[arg0]); delete(@reqstack, arg0); delete(@reqts, arg0); } END { clear(@reqstack); clear(@reqts); } bpftrace-0.23.2/tools/old/mdflush.bt000077500000000000000000000013171477746507000173210ustar00rootroot00000000000000#!/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); } bpftrace-0.23.2/tools/old/tcpdrop.bt000077500000000000000000000046071477746507000173370ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpdrop.bt Trace TCP kernel-dropped packets/segments. * For Linux, uses bpftrace and eBPF. * * USAGE: tcpdrop.bt * * This is a bpftrace version of the bcc tool of the same name. * * This provides information such as packet details, socket state, and kernel * stack trace for packets/segments that were dropped via tcp_drop(). * It cannot show tcp flags. * 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 * * For Linux <= 5.18. * * 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 { printf("Tracing tcp drops. Hit Ctrl-C to end.\n"); printf("%-8s %-8s %-16s %-21s %-21s %-8s\n", "TIME", "PID", "COMM", "SADDR:SPORT", "DADDR:DPORT", "STATE"); // See https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h @tcp_states[1] = "ESTABLISHED"; @tcp_states[2] = "SYN_SENT"; @tcp_states[3] = "SYN_RECV"; @tcp_states[4] = "FIN_WAIT1"; @tcp_states[5] = "FIN_WAIT2"; @tcp_states[6] = "TIME_WAIT"; @tcp_states[7] = "CLOSE"; @tcp_states[8] = "CLOSE_WAIT"; @tcp_states[9] = "LAST_ACK"; @tcp_states[10] = "LISTEN"; @tcp_states[11] = "CLOSING"; @tcp_states[12] = "NEW_SYN_RECV"; } kprobe:tcp_drop { $sk = ((struct sock *) arg0); $inet_family = $sk->__sk_common.skc_family; if ($inet_family == AF_INET || $inet_family == AF_INET6) { if ($inet_family == AF_INET) { $daddr = ntop($sk->__sk_common.skc_daddr); $saddr = ntop($sk->__sk_common.skc_rcv_saddr); } else { $daddr = ntop($sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); $saddr = ntop($sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); } $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped $dport = bswap($dport); $state = $sk->__sk_common.skc_state; $statestr = @tcp_states[$state]; time("%H:%M:%S "); printf("%-8d %-16s ", pid, comm); printf("%39s:%-6d %39s:%-6d %-10s\n", $saddr, $lport, $daddr, $dport, $statestr); printf("%s\n", kstack); } } END { clear(@tcp_states); } bpftrace-0.23.2/tools/oomkill.bt000077500000000000000000000022561477746507000165520ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * oomkill Trace OOM killer. * For Linux, uses bpftrace and eBPF. * * This traces the kernel out-of-memory killer, and prints basic details, * including the system load averages. This can provide more context on the * system state at the time of OOM: was it getting busier or steady, based * on the load averages? This tool may also be useful to customize for * investigations; for example, by adding other task_struct details at the * time of the OOM, or other commands in the system() call. * * This currently works by using kernel dynamic tracing of oom_kill_process(). * * USAGE: oomkill.bt * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 07-Sep-2018 Brendan Gregg Created this. */ #ifndef BPFTRACE_HAVE_BTF #include #endif BEGIN { printf("Tracing oom_kill_process()... Hit Ctrl-C to end.\n"); } kprobe:oom_kill_process { $oc = (struct oom_control *)arg0; time("%H:%M:%S "); printf("Triggered by PID %d (\"%s\"), ", pid, comm); printf("OOM kill of PID %d (\"%s\"), %d pages, loadavg: ", $oc->chosen->pid, $oc->chosen->comm, $oc->totalpages); cat("/proc/loadavg"); } bpftrace-0.23.2/tools/oomkill_example.txt000066400000000000000000000032041477746507000204660ustar00rootroot00000000000000Demonstrations of oomkill, the Linux bpftrace/eBPF version. oomkill is a simple program that traces the Linux out-of-memory (OOM) killer, and shows basic details on one line per OOM kill: # ./oomkill.bt Tracing oom_kill_process()... Ctrl-C to end. 21:03:39 Triggered by PID 3297 ("ntpd"), OOM kill of PID 22516 ("perl"), 3850642 pages, loadavg: 0.99 0.39 0.30 3/282 22724 21:03:48 Triggered by PID 22517 ("perl"), OOM kill of PID 22517 ("perl"), 3850642 pages, loadavg: 0.99 0.41 0.30 2/282 22932 The first line shows that PID 22516, with process name "perl", was OOM killed when it reached 3850642 pages (usually 4 Kbytes per page). This OOM kill happened to be triggered by PID 3297, process name "ntpd", doing some memory allocation. The system log (dmesg) shows pages of details and system context about an OOM kill. What it currently lacks, however, is context on how the system had been changing over time. I've seen OOM kills where I wanted to know if the system was at steady state at the time, or if there had been a recent increase in workload that triggered the OOM event. oomkill provides some context: at the end of the line is the load average information from /proc/loadavg. For both of the oomkills here, we can see that the system was getting busier at the time (a higher 1 minute "average" of 0.99, compared to the 15 minute "average" of 0.30). oomkill can also be the basis of other tools and customizations. For example, you can edit it to include other task_struct details from the target PID at the time of the OOM kill, or to run other commands from the shell. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.23.2/tools/opensnoop.bt000077500000000000000000000016711477746507000171240ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * opensnoop Trace open() syscalls. * For Linux, uses bpftrace and eBPF. * * Also a basic example of bpftrace. * * USAGE: opensnoop.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing open syscalls... Hit Ctrl-C to end.\n"); printf("%-6s %-16s %4s %3s %s\n", "PID", "COMM", "FD", "ERR", "PATH"); } tracepoint:syscalls:sys_enter_open, tracepoint:syscalls:sys_enter_openat { @filename[tid] = args.filename; } tracepoint:syscalls:sys_exit_open, tracepoint:syscalls:sys_exit_openat /@filename[tid]/ { $ret = args.ret; $fd = $ret >= 0 ? $ret : -1; $errno = $ret >= 0 ? 0 : - $ret; printf("%-6d %-16s %4d %3d %s\n", pid, comm, $fd, $errno, str(@filename[tid])); delete(@filename, tid); } END { clear(@filename); } bpftrace-0.23.2/tools/opensnoop_example.txt000066400000000000000000000047401477746507000210460ustar00rootroot00000000000000Demonstrations of opensnoop, the Linux bpftrace/eBPF version. opensnoop traces the open() syscall system-wide, and prints various details. Example output: # ./opensnoop.bt Attaching 3 probes... Tracing open syscalls... Hit Ctrl-C to end. PID COMM FD ERR PATH 2440 snmp-pass 4 0 /proc/cpuinfo 2440 snmp-pass 4 0 /proc/stat 25706 ls 3 0 /etc/ld.so.cache 25706 ls 3 0 /lib/x86_64-linux-gnu/libselinux.so.1 25706 ls 3 0 /lib/x86_64-linux-gnu/libc.so.6 25706 ls 3 0 /lib/x86_64-linux-gnu/libpcre.so.3 25706 ls 3 0 /lib/x86_64-linux-gnu/libdl.so.2 25706 ls 3 0 /lib/x86_64-linux-gnu/libpthread.so.0 25706 ls 3 0 /proc/filesystems 25706 ls 3 0 /usr/lib/locale/locale-archive 25706 ls 3 0 . 1744 snmpd 8 0 /proc/net/dev 1744 snmpd 21 0 /proc/net/if_inet6 1744 snmpd 21 0 /sys/class/net/eth0/device/vendor 1744 snmpd 21 0 /sys/class/net/eth0/device/device 1744 snmpd 21 0 /proc/sys/net/ipv4/neigh/eth0/retrans_time_ms 1744 snmpd 21 0 /proc/sys/net/ipv6/neigh/eth0/retrans_time_ms 1744 snmpd 21 0 /proc/sys/net/ipv6/conf/eth0/forwarding 1744 snmpd 21 0 /proc/sys/net/ipv6/neigh/eth0/base_reachable_time_ms 1744 snmpd -1 2 /sys/class/net/lo/device/vendor 1744 snmpd 21 0 /proc/sys/net/ipv4/neigh/lo/retrans_time_ms 1744 snmpd 21 0 /proc/sys/net/ipv6/neigh/lo/retrans_time_ms 1744 snmpd 21 0 /proc/sys/net/ipv6/conf/lo/forwarding 1744 snmpd 21 0 /proc/sys/net/ipv6/neigh/lo/base_reachable_time_ms 2440 snmp-pass 4 0 /proc/cpuinfo 2440 snmp-pass 4 0 /proc/stat 22884 pickup 12 0 maildrop 2440 snmp-pass 4 0 /proc/cpuinfo 2440 snmp-pass 4 0 /proc/stat While tracing, at "ls" command was launched: the libraries it uses can be seen as they were opened. Also, the snmpd process opened various /proc and /sys files (reading metrics). was starting up: a new process). opensnoop can be useful for discovering configuration and log files, if used during application startup. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides command line options to customize the output. bpftrace-0.23.2/tools/pidpersec.bt000077500000000000000000000011641477746507000170570ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * pidpersec Count new processes (via fork). * For Linux, uses bpftrace and eBPF. * * Written as a basic example of counting on an event. * * USAGE: pidpersec.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing new processes... Hit Ctrl-C to end.\n"); } tracepoint:sched:sched_process_fork { @ = count(); } interval:s:1 { time("%H:%M:%S PIDs/sec: "); print(@); clear(@); } END { clear(@); } bpftrace-0.23.2/tools/pidpersec_example.txt000066400000000000000000000027401477746507000210020ustar00rootroot00000000000000Demonstrations of pidpersec, the Linux bpftrace/eBPF version. Tracing new processes: # ./pidpersec.bt Attaching 4 probes... Tracing new processes... Hit Ctrl-C to end. 22:29:50 PIDs/sec: @: 121 22:29:51 PIDs/sec: @: 120 22:29:52 PIDs/sec: @: 122 22:29:53 PIDs/sec: @: 124 22:29:54 PIDs/sec: @: 123 22:29:55 PIDs/sec: @: 121 22:29:56 PIDs/sec: @: 121 22:29:57 PIDs/sec: @: 121 22:29:58 PIDs/sec: @: 49 22:29:59 PIDs/sec: 22:30:00 PIDs/sec: 22:30:01 PIDs/sec: 22:30:02 PIDs/sec: ^C The output begins by showing a rate of new processes over 120 per second. That then ends at time 22:29:59, and for the next few seconds there are zero new processes per second. The following example shows a Linux build launched at 6:33:40, on a 36 CPU server, with make -j36: # ./pidpersec.bt Attaching 4 probes... Tracing new processes... Hit Ctrl-C to end. 06:33:38 PIDs/sec: 06:33:39 PIDs/sec: 06:33:40 PIDs/sec: @: 2314 06:33:41 PIDs/sec: @: 2517 06:33:42 PIDs/sec: @: 1345 06:33:43 PIDs/sec: @: 1752 06:33:44 PIDs/sec: @: 1744 06:33:45 PIDs/sec: @: 1549 06:33:46 PIDs/sec: @: 1643 06:33:47 PIDs/sec: @: 1487 06:33:48 PIDs/sec: @: 1534 06:33:49 PIDs/sec: @: 1279 06:33:50 PIDs/sec: @: 1392 06:33:51 PIDs/sec: @: 1556 06:33:52 PIDs/sec: @: 1580 06:33:53 PIDs/sec: @: 1944 A Linux kernel build involves launched many thousands of short-lived processes, which can be seen in the above output: a rate of over 1,000 processes per second. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.23.2/tools/runqlat.bt000077500000000000000000000021331477746507000165640ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * runqlat.bt CPU scheduler run queue latency as a histogram. * For Linux, uses bpftrace, eBPF. * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 17-Sep-2018 Brendan Gregg Created this. */ #ifndef BPFTRACE_HAVE_BTF #include #else /* * With BTF providing types, full headers are not needed. * We only need to supply the preprocessor defines used in this script. * TASK_RUNNING is not arch-dependant and has not changed in the linux * git history (it is not part of the stable API though) */ #define TASK_RUNNING 0 #endif BEGIN { printf("Tracing CPU scheduler... Hit Ctrl-C to end.\n"); } tracepoint:sched:sched_wakeup, tracepoint:sched:sched_wakeup_new { @qtime[args.pid] = nsecs; } tracepoint:sched:sched_switch { if (args.prev_state == TASK_RUNNING) { @qtime[args.prev_pid] = nsecs; } $ns = @qtime[args.next_pid]; if ($ns) { @usecs = hist((nsecs - $ns) / 1000); } delete(@qtime, args.next_pid); } END { clear(@qtime); } bpftrace-0.23.2/tools/runqlat_example.txt000066400000000000000000000206701477746507000205140ustar00rootroot00000000000000Demonstrations of runqlat, the Linux BPF/bpftrace version. This traces time spent waiting in the CPU scheduler for a turn on-CPU. This metric is often called run queue latency, or scheduler latency. This tool shows this latency as a power-of-2 histogram in nanoseconds. For example: # ./runqlat.bt Attaching 5 probes... Tracing CPU scheduler... Hit Ctrl-C to end. ^C @usecs: [0] 1 | | [1] 11 |@@ | [2, 4) 16 |@@@ | [4, 8) 43 |@@@@@@@@@@ | [8, 16) 134 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [16, 32) 220 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [32, 64) 117 |@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [64, 128) 84 |@@@@@@@@@@@@@@@@@@@ | [128, 256) 10 |@@ | [256, 512) 2 | | [512, 1K) 5 |@ | [1K, 2K) 5 |@ | [2K, 4K) 5 |@ | [4K, 8K) 4 | | [8K, 16K) 1 | | [16K, 32K) 2 | | [32K, 64K) 0 | | [64K, 128K) 1 | | [128K, 256K) 0 | | [256K, 512K) 0 | | [512K, 1M) 1 | | This is an idle system where most of the time we are waiting for less than 128 microseconds, shown by the mode above. As an example of reading the output, the above histogram shows 220 scheduling events with a run queue latency of between 16 and 32 microseconds. The output also shows an outlier taking between 0.5 and 1 seconds: ??? XXX likely work was scheduled behind another higher priority task, and had to wait briefly. The kernel decides whether it is worth migrating such work to an idle CPU, or leaving it wait its turn on its current CPU run queue where the CPU caches should be hotter. I'll now add a single-threaded CPU bound workload to this system, and bind it on one CPU: # ./runqlat.bt Attaching 5 probes... Tracing CPU scheduler... Hit Ctrl-C to end. ^C @usecs: [1] 6 |@@@ | [2, 4) 26 |@@@@@@@@@@@@@ | [4, 8) 97 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [8, 16) 72 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [16, 32) 17 |@@@@@@@@@ | [32, 64) 19 |@@@@@@@@@@ | [64, 128) 20 |@@@@@@@@@@ | [128, 256) 3 |@ | [256, 512) 0 | | [512, 1K) 0 | | [1K, 2K) 1 | | [2K, 4K) 1 | | [4K, 8K) 4 |@@ | [8K, 16K) 3 |@ | [16K, 32K) 0 | | [32K, 64K) 0 | | [64K, 128K) 0 | | [128K, 256K) 1 | | [256K, 512K) 0 | | [512K, 1M) 0 | | [1M, 2M) 1 | | That didn't make much difference. Now I'll add a second single-threaded CPU workload, and bind it to the same CPU, causing contention: # ./runqlat.bt Attaching 5 probes... Tracing CPU scheduler... Hit Ctrl-C to end. ^C @usecs: [0] 1 | | [1] 8 |@@@ | [2, 4) 28 |@@@@@@@@@@@@ | [4, 8) 95 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [8, 16) 120 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [16, 32) 22 |@@@@@@@@@ | [32, 64) 10 |@@@@ | [64, 128) 7 |@@@ | [128, 256) 3 |@ | [256, 512) 1 | | [512, 1K) 0 | | [1K, 2K) 0 | | [2K, 4K) 2 | | [4K, 8K) 4 |@ | [8K, 16K) 107 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [16K, 32K) 0 | | [32K, 64K) 0 | | [64K, 128K) 0 | | [128K, 256K) 0 | | [256K, 512K) 1 | | There's now a second mode between 8 and 16 milliseconds, as each thread must wait its turn on the one CPU. Now I'll run 10 CPU-bound threads on one CPU: # ./runqlat.bt Attaching 5 probes... Tracing CPU scheduler... Hit Ctrl-C to end. ^C @usecs: [0] 2 | | [1] 10 |@ | [2, 4) 38 |@@@@ | [4, 8) 63 |@@@@@@ | [8, 16) 106 |@@@@@@@@@@@ | [16, 32) 28 |@@@ | [32, 64) 13 |@ | [64, 128) 15 |@ | [128, 256) 2 | | [256, 512) 2 | | [512, 1K) 1 | | [1K, 2K) 1 | | [2K, 4K) 2 | | [4K, 8K) 4 | | [8K, 16K) 3 | | [16K, 32K) 0 | | [32K, 64K) 478 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [64K, 128K) 1 | | [128K, 256K) 0 | | [256K, 512K) 0 | | [512K, 1M) 0 | | [1M, 2M) 1 | | This shows that most of the time threads need to wait their turn, with the largest mode between 32 and 64 milliseconds. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.23.2/tools/runqlen.bt000077500000000000000000000020251477746507000165620ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * runqlen.bt CPU scheduler run queue length as a histogram. * For Linux, uses bpftrace, eBPF. * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 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 { struct load_weight load; unsigned int nr_running; unsigned int h_nr_running; }; #endif BEGIN { printf("Sampling run queue length at 99 Hertz... Hit Ctrl-C to end.\n"); } profile:hz:99 { $task = (struct task_struct *)curtask; $my_q = (struct cfs_rq *)$task->se.cfs_rq; $len = (uint64)$my_q->nr_running; $len = $len > 0 ? $len - 1 : 0; // subtract currently running task @runqlen = lhist($len, 0, 100, 1); } bpftrace-0.23.2/tools/runqlen_example.txt000066400000000000000000000017221477746507000205070ustar00rootroot00000000000000Demonstrations of runqlen, the Linux BPF/bpftrace version. This tool samples the length of the CPU scheduler run queues, showing these sampled lengths as a histogram. This can be used to characterize demand for CPU resources. For example: # ./runqlen.bt Attaching 2 probes... Sampling run queue length at 99 Hertz... Hit Ctrl-C to end. ^C @runqlen: [0, 1) 1967 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [1, 2) 0 | | [2, 3) 0 | | [3, 4) 306 |@@@@@@@@ | This output shows that the run queue length was usually zero, except for some samples where it was 3. This was caused by binding 4 CPU bound threads to a single CPUs. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.23.2/tools/setuids.bt000077500000000000000000000034071477746507000165630ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * setuids - Trace the setuid syscalls: privilege escalation. * * See BPF Performance Tools, Chapter 11, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 26-Feb-2019 Brendan Gregg Created this. */ BEGIN { printf("Tracing setuid(2) family syscalls. Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-16s %-6s %-9s %s\n", "TIME", "PID", "COMM", "UID", "SYSCALL", "ARGS (RET)"); } tracepoint:syscalls:sys_enter_setuid, tracepoint:syscalls:sys_enter_setfsuid { @uid[tid] = uid; @setuid[tid] = args.uid; @seen[tid] = 1; } tracepoint:syscalls:sys_enter_setresuid { @uid[tid] = uid; @ruid[tid] = args.ruid; @euid[tid] = args.euid; @suid[tid] = args.suid; @seen[tid] = 1; } tracepoint:syscalls:sys_exit_setuid /@seen[tid]/ { time("%H:%M:%S "); printf("%-6d %-16s %-6d setuid uid=%d (%d)\n", pid, comm, @uid[tid], @setuid[tid], args.ret); delete(@seen, tid); delete(@uid, tid); delete(@setuid, tid); } tracepoint:syscalls:sys_exit_setfsuid /@seen[tid]/ { time("%H:%M:%S "); printf("%-6d %-16s %-6d setfsuid uid=%d (prevuid=%d)\n", pid, comm, @uid[tid], @setuid[tid], args.ret); delete(@seen, tid); delete(@uid, tid); delete(@setuid, tid); } tracepoint:syscalls:sys_exit_setresuid /@seen[tid]/ { time("%H:%M:%S "); printf("%-6d %-16s %-6d setresuid ", pid, comm, @uid[tid]); printf("ruid=%d euid=%d suid=%d (%d)\n", @ruid[tid], @euid[tid], @suid[tid], args.ret); delete(@seen, tid); delete(@uid, tid); delete(@ruid, tid); delete(@euid, tid); delete(@suid, tid); } bpftrace-0.23.2/tools/setuids_example.txt000066400000000000000000000046111477746507000205030ustar00rootroot00000000000000Demonstrations of setuids, the Linux bpftrace/eBPF version. This tool traces privilege escalation via setuid syscalls (setuid(2), setfsuid(2), retresuid(2)). For example, here are the setuid calls during an ssh login: # ./setuids.bt Attaching 7 probes... Tracing setuid(2) family syscalls. Hit Ctrl-C to end. TIME PID COMM UID SYSCALL ARGS (RET) 14:28:22 21785 ssh 1000 setresuid ruid=-1 euid=1000 suid=-1 (0) 14:28:22 21787 sshd 0 setresuid ruid=122 euid=122 suid=122 (0) 14:28:22 21787 sshd 122 setuid uid=0 (-1) 14:28:22 21787 sshd 122 setresuid ruid=-1 euid=0 suid=-1 (-1) 14:28:24 21786 sshd 0 setresuid ruid=-1 euid=1000 suid=-1 (0) 14:28:24 21786 sshd 0 setresuid ruid=-1 euid=0 suid=-1 (0) 14:28:24 21786 sshd 0 setresuid ruid=-1 euid=1000 suid=-1 (0) 14:28:24 21786 sshd 0 setresuid ruid=-1 euid=0 suid=-1 (0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=0) 14:28:24 21786 sshd 0 setfsuid uid=1000 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=1000) 14:28:24 21786 sshd 0 setfsuid uid=0 (prevuid=0) 14:28:24 21851 sshd 0 setresuid ruid=1000 euid=1000 suid=1000 (0) 14:28:24 21851 sshd 1000 setuid uid=0 (-1) 14:28:24 21851 sshd 1000 setresuid ruid=-1 euid=0 suid=-1 (-1) Why does sshd make so many calls? I don't know! Nevertheless, this shows what this tool can do: it shows the caller details (PID, COMM, and UID), the syscall (SYSCALL), and the syscall arguments (ARGS) and return value (RET). You can modify this tool to print user stack traces for each call, which will show the code path in sshd (provided it is compiled with frame pointers). bpftrace-0.23.2/tools/ssllatency.bt000077500000000000000000000041751477746507000172670ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * ssllatency Trace SSL/TLS handshake for OpenSSL. * For Linux, uses bpftrace and eBPF. * * ssllatency shows handshake latency stats and distribution. * * Copyright (c) 2021 Tao Xu. * Licensed under the Apache License, Version 2.0 (the "License") * * 17-Dec-2021 Tao Xu created this. */ config = { missing_probes = "ignore" } BEGIN { printf("Tracing SSL/TLS handshake... Hit Ctrl-C to end.\n"); } uprobe:libssl:SSL_read, uprobe:libssl:SSL_write, uprobe:libssl:SSL_do_handshake { @start_ssl[tid] = nsecs; @func_ssl[tid] = func; // store for uretprobe } uretprobe:libssl:SSL_read, uretprobe:libssl:SSL_write, uretprobe:libssl:SSL_do_handshake /@start_ssl[tid] != 0/ { $lat_us = (nsecs - @start_ssl[tid]) / 1000; if ((int8)retval >= 1) { @hist[@func_ssl[tid]] = lhist($lat_us, 0, 1000, 200); @stat[@func_ssl[tid]] = stats($lat_us); } else { @histF[@func_ssl[tid]] = lhist($lat_us, 0, 1000, 200); @statF[@func_ssl[tid]] = stats($lat_us); } delete(@start_ssl, tid); delete(@func_ssl, tid); } // need debug symbol for ossl local functions uprobe:libcrypto:rsa_ossl_public_encrypt, uprobe:libcrypto:rsa_ossl_public_decrypt, uprobe:libcrypto:rsa_ossl_private_encrypt, uprobe:libcrypto:rsa_ossl_private_decrypt, uprobe:libcrypto:RSA_sign, uprobe:libcrypto:RSA_verify, uprobe:libcrypto:ossl_ecdsa_sign, uprobe:libcrypto:ossl_ecdsa_verify, uprobe:libcrypto:ecdh_simple_compute_key { @start_crypto[tid] = nsecs; @func_crypto[tid] = func; // store for uretprobe } uretprobe:libcrypto:rsa_ossl_public_encrypt, uretprobe:libcrypto:rsa_ossl_public_decrypt, uretprobe:libcrypto:rsa_ossl_private_encrypt, uretprobe:libcrypto:rsa_ossl_private_decrypt, uretprobe:libcrypto:RSA_sign, uretprobe:libcrypto:RSA_verify, uretprobe:libcrypto:ossl_ecdsa_sign, uretprobe:libcrypto:ossl_ecdsa_verify, uretprobe:libcrypto:ecdh_simple_compute_key /@start_crypto[tid] != 0/ { $lat_us = (nsecs - @start_crypto[tid]) / 1000; @hist[@func_crypto[tid]] = lhist($lat_us, 0, 1000, 200); @stat[@func_crypto[tid]] = stats($lat_us); delete(@start_crypto, tid); delete(@func_crypto, tid); } END { printf("\nLatency distribution in microsecond:"); } bpftrace-0.23.2/tools/ssllatency_example.txt000066400000000000000000000106361477746507000212100ustar00rootroot00000000000000Demonstrations of ssllatency, the Linux bpftrace/eBPF version. ssllatency traces OpenSSL handshake functions. It's the statistical summary version of sslsnoop. This is useful for performance analysis with different crypto algorithms or async SSL acceleration by CPU or offload device. For example: # wrk -t 1 -c 10 -d 1s -H 'Connection: close' https://localhost:443/0kb.bin Running 1s test @ https://localhost:443/0kb.bin 1 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 839.94us 323.68us 5.98ms 98.50% Req/Sec 1.28k 9.05 1.29k 54.55% 1400 requests in 1.10s, 414.26KB read Non-2xx or 3xx responses: 1400 Requests/sec: 1272.97 Transfer/sec: 376.67KB # ./ssllatency.bt Attaching 26 probes... Tracing SSL/TLS handshake... Hit Ctrl-C to end. ^C Latency distribution in microsecond: @hist[SSL_write]: [0, 200) 1401 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @hist[SSL_read]: [0, 200) 1401 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @hist[SSL_do_handshake]: [600, 800) 1359 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [800, 1000) 12 | | [1000, ...) 32 |@ | @hist[rsa_ossl_private_decrypt]: [600, 800) 1359 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [800, 1000) 44 |@ | @histF[SSL_do_handshake]: [0, 200) 1410 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @histF[SSL_read]: [0, 200) 2804 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @stat[SSL_read]: count 1401, average 2, total 2834 @stat[SSL_write]: count 1401, average 5, total 7062 @stat[rsa_ossl_private_decrypt]: count 1403, average 643, total 902780 @stat[SSL_do_handshake]: count 1403, average 706, total 991605 @statF[SSL_read]: count 2804, average 1, total 3951 @statF[SSL_do_handshake]: count 1410, average 29, total 41964 This output shows latency distribution for wrk benchmark which saturated one CPU core used by nginx server. wrk issued 1400 requests in 1.10s, and req/s is 1272. Server side RSA function is counted 1403 times averaging 643us latency. And there's same amount(1410/1403) of failed/successful SSL_do_handshake calls for the round trip. This is the default behavior. # wrk -t 1 -c 10 -d 1s -H 'Connection: close' https://localhost:443/0kb.bin Running 1s test @ https://localhost:443/0kb.bin 1 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 448.67us 148.67us 1.28ms 82.00% Req/Sec 2.95k 43.03 2.99k 80.00% 2933 requests in 1.00s, 867.87KB read Non-2xx or 3xx responses: 2933 Requests/sec: 2930.53 Transfer/sec: 867.14KB # ./ssllatency.bt Attaching 26 probes... Tracing SSL/TLS handshake... Hit Ctrl-C to end. ^C Latency distribution in microsecond: @hist[SSL_write]: [0, 200) 2933 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @hist[SSL_read]: [0, 200) 2933 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @hist[SSL_do_handshake]: [0, 200) 2941 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @histF[SSL_read]: [0, 200) 5873 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @histF[SSL_do_handshake]: [0, 200) 5884 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @stat[SSL_read]: count 2933, average 4, total 12088 @stat[SSL_write]: count 2933, average 7, total 20683 @stat[SSL_do_handshake]: count 2941, average 51, total 151149 @statF[SSL_read]: count 5873, average 2, total 13942 @statF[SSL_do_handshake]: count 5884, average 19, total 113061 This is the hardware accelerated result by using async SSL and CPU crypto SIMD. req/s is more than doubled under same wkr workload. Peak throughput can be more than 3x if adding more wrk connections. Keep using same workload for comparison. libcrypto_mb.so is used instead of libcrypto.so, to batch process multiple async requests simultaneously by SIMD. As a result, wrk issued 2933 requests in 1s. Failed SSL_do_handshake calls has doubled(5884), and successful calls(2941) returned quickly(51us). This is expected from async routines. The above effect is based on the huge bottleneck of RSA which is very CPU intensive. If change to ECDSA, the overhead would be much less, and overall improvement is less obvious. bpftrace-0.23.2/tools/sslsnoop.bt000077500000000000000000000040411477746507000167560ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * sslsnoop Trace SSL/TLS handshake for OpenSSL. * For Linux, uses bpftrace and eBPF. * * sslsnoop shows handshake latency and retval. This is useful for SSL/TLS * performance analysis. * * Copyright (c) 2021 Tao Xu. * Licensed under the Apache License, Version 2.0 (the "License") * * 15-Dec-2021 Tao Xu created this. */ config = { missing_probes = "ignore" } BEGIN { printf("Tracing SSL/TLS handshake... Hit Ctrl-C to end.\n"); printf("%-10s %-8s %-8s %7s %5s %s\n", "TIME(us)", "TID", "COMM", "LAT(us)", "RET", "FUNC"); } uprobe:libssl:SSL_read, uprobe:libssl:SSL_write, uprobe:libssl:SSL_do_handshake { @start_ssl[tid] = nsecs; @func_ssl[tid] = func; // store for uretprobe } uretprobe:libssl:SSL_read, uretprobe:libssl:SSL_write, uretprobe:libssl:SSL_do_handshake /@start_ssl[tid] != 0/ { printf("%-10u %-8d %-8s %7u %5d %s\n", elapsed/1000, tid, comm, (nsecs - @start_ssl[tid])/1000, retval, @func_ssl[tid]); delete(@start_ssl, tid); delete(@func_ssl, tid); } // need debug symbol for ossl local functions uprobe:libcrypto:rsa_ossl_public_encrypt, uprobe:libcrypto:rsa_ossl_public_decrypt, uprobe:libcrypto:rsa_ossl_private_encrypt, uprobe:libcrypto:rsa_ossl_private_decrypt, uprobe:libcrypto:RSA_sign, uprobe:libcrypto:RSA_verify, uprobe:libcrypto:ossl_ecdsa_sign, uprobe:libcrypto:ossl_ecdsa_verify, uprobe:libcrypto:ossl_ecdh_compute_key { @start_crypto[tid] = nsecs; @func_crypto[tid] = func; // store for uretprobe } uretprobe:libcrypto:rsa_ossl_public_encrypt, uretprobe:libcrypto:rsa_ossl_public_decrypt, uretprobe:libcrypto:rsa_ossl_private_encrypt, uretprobe:libcrypto:rsa_ossl_private_decrypt, uretprobe:libcrypto:RSA_sign, uretprobe:libcrypto:RSA_verify, uretprobe:libcrypto:ossl_ecdsa_sign, uretprobe:libcrypto:ossl_ecdsa_verify, uretprobe:libcrypto:ossl_ecdh_compute_key /@start_crypto[tid] != 0/ { printf("%-10u %-8d %-8s %7u %5d %s\n", elapsed/1000, tid, comm, (nsecs - @start_crypto[tid])/1000, retval, @func_crypto[tid]); delete(@start_crypto, tid); delete(@func_crypto, tid); } bpftrace-0.23.2/tools/sslsnoop_example.txt000066400000000000000000000035741477746507000207120ustar00rootroot00000000000000Demonstrations of sslsnoop, the Linux bpftrace/eBPF version. sslsnoop shows OpenSSL handshake function latency and return value. This can be used to analyzea SSL/TLS performance. For example: # ./sslsnoop.bt Attaching 25 probes... Tracing SSL/TLS handshake... Hit Ctrl-C to end. TIME(us) TID COMM LAT(us) RET FUNC 1623016 2834695 openssl 71 1 ossl_ecdh_compute_key 1623319 2834695 openssl 32 51 rsa_ossl_public_decrypt 1623418 2834695 openssl 31 51 rsa_ossl_public_decrypt 1623547 2834695 openssl 27 256 rsa_ossl_public_decrypt 1623612 2834695 openssl 361150 0 SSL_write 1804646 2834695 openssl 92 -1 SSL_read 1804730 2834695 openssl 76 -1 SSL_read ^C Above shows the output of 'openssl s_client -connect example.com:443'. The first SSL_write call returned after 361ms that is the TLS handshake time. Local ECDH and RSA crypto calculation is fast at client side. Most time is spent at server side including crypto and network latency. # ./sslsnoop.bt Attaching 25 probes... Tracing SSL/TLS handshake... Hit Ctrl-C to end. TIME(us) TID COMM LAT(us) RET FUNC 1133960 2826460 nginx 81 -1 SSL_do_handshake 1134910 2826460 nginx 631 256 rsa_ossl_private_decrypt 1134977 2826460 nginx 709 1 SSL_do_handshake 1134984 2826460 nginx 3 -1 SSL_read 1134209 2834970 openssl 37 256 rsa_ossl_public_encrypt 1134994 2834970 openssl 1244 0 SSL_write ^C Change example.com to localhost to exclude network latency. Output above shows 1.2ms overall handshake time, and RSA calculation took 0.7ms at nginx server. As event print is asynchronous, timestamp is not guaranteed to show in order. The bcc tool sslsniff shows similar event latency, and additional plaintext and ciphertext in SSL_read/wirte: https://github.com/iovisor/bcc bpftrace-0.23.2/tools/statsnoop.bt000077500000000000000000000024161477746507000171340ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * statsnoop Trace stat() syscalls. * For Linux, uses bpftrace and eBPF. * * This traces the tracepoints for statfs(), statx(), newstat(), and * newlstat(). These aren't the only the stat syscalls: if you are missing * activity, you may need to add more variants. * * Also a basic example of bpftrace. * * USAGE: statsnoop.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing stat syscalls... Hit Ctrl-C to end.\n"); printf("%-6s %-16s %3s %s\n", "PID", "COMM", "ERR", "PATH"); } tracepoint:syscalls:sys_enter_statfs { @filename[tid] = args.pathname; } tracepoint:syscalls:sys_enter_statx, tracepoint:syscalls:sys_enter_newstat, tracepoint:syscalls:sys_enter_newlstat { @filename[tid] = args.filename; } tracepoint:syscalls:sys_exit_statfs, tracepoint:syscalls:sys_exit_statx, tracepoint:syscalls:sys_exit_newstat, tracepoint:syscalls:sys_exit_newlstat /@filename[tid]/ { $ret = args.ret; $errno = $ret >= 0 ? 0 : - $ret; printf("%-6d %-16s %3d %s\n", pid, comm, $errno, str(@filename[tid])); delete(@filename, tid); } END { clear(@filename); } bpftrace-0.23.2/tools/statsnoop_example.txt000066400000000000000000000052621477746507000210600ustar00rootroot00000000000000Demonstrations of statsnoop, the Linux bpftrace/eBPF version. statsnoop traces different stat() syscalls system-wide, and prints details. Example output: # ./statsnoop.bt Attaching 9 probes... Tracing stat syscalls... Hit Ctrl-C to end. PID COMM ERR PATH 27835 bash 0 . 27835 bash 2 /usr/local/sbin/iconfig 27835 bash 2 /usr/local/bin/iconfig 27835 bash 2 /usr/sbin/iconfig 27835 bash 2 /usr/bin/iconfig 27835 bash 2 /sbin/iconfig 27835 bash 2 /bin/iconfig 27835 bash 2 /usr/games/iconfig 27835 bash 2 /usr/local/games/iconfig 27835 bash 2 /snap/bin/iconfig 27835 bash 2 /apps/python/bin/iconfig 30573 command-not-fou 2 /usr/bin/Modules/Setup 30573 command-not-fou 2 /usr/bin/lib/python3.5/os.py 30573 command-not-fou 2 /usr/bin/lib/python3.5/os.pyc 30573 command-not-fou 0 /usr/lib/python3.5/os.py 30573 command-not-fou 2 /usr/bin/pybuilddir.txt 30573 command-not-fou 2 /usr/bin/lib/python3.5/lib-dynload 30573 command-not-fou 0 /usr/lib/python3.5/lib-dynload 30573 command-not-fou 2 /usr/lib/python35.zip 30573 command-not-fou 0 /usr/lib 30573 command-not-fou 2 /usr/lib/python35.zip 30573 command-not-fou 0 /usr/lib/python3.5/ 30573 command-not-fou 0 /usr/lib/python3.5/ 30573 command-not-fou 0 /usr/lib/python3.5/ 30573 command-not-fou 2 /usr/lib/python3.5/encodings/__init__.cpython-35m-x86_64-linux- 30573 command-not-fou 2 /usr/lib/python3.5/encodings/__init__.abi3.so 30573 command-not-fou 2 /usr/lib/python3.5/encodings/__init__.so 30573 command-not-fou 0 /usr/lib/python3.5/encodings/__init__.py 30573 command-not-fou 0 /usr/lib/python3.5/encodings/__init__.py This output has caught me mistyping a command in another shell, "iconfig" instead of "ifconfig". The first several lines show the bash shell searching the $PATH (why is games in my $PATH??), and failing to find it (ERR == 2 is file not found). Then, a "command-not-found" program executes (the name is truncated to 16 characters in the COMM field, including the NULL), which begins the process of searching for and suggesting a package. ie, this: # iconfig The program 'iconfig' is currently not installed. You can install it by typing: apt install ipmiutil statsnoop can be used for general debugging, to see what file information has been requested, and whether those files exist. It can be used as a companion to opensnoop, which shows what files were actually opened. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides options to customize the output. bpftrace-0.23.2/tools/swapin.bt000077500000000000000000000016621477746507000164050ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * swapin - Show swapins by process. * * See BPF Performance Tools, Chapter 7, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 26-Jan-2019 Brendan Gregg Created this. * 31-May-2024 Rong Tao Add folio support. */ config = { missing_probes = "ignore" } /** * kernel commit c9bdf768dd93("mm: convert swap_readpage() to swap_read_folio()") * convert swap_readpage() to swap_read_folio(), try attaching two kprobes, * only one will succeed and the other will be silently ignored. */ kprobe:swap_readpage, kprobe:swap_read_folio { @[comm, pid] = count(); } interval:s:1 { time(); print(@); clear(@); } bpftrace-0.23.2/tools/swapin_example.txt000066400000000000000000000010441477746507000203210ustar00rootroot00000000000000Demonstrations of swapin, the Linux BCC/eBPF version. This tool counts swapins by process, to show which process is affected by swapping. For example: # ./swapin.bt Attaching 2 probes... 13:36:59 13:37:00 @[chrome, 4536]: 10809 @[gnome-shell, 2239]: 12410 13:37:01 @[chrome, 4536]: 3826 13:37:02 @[cron, 1180]: 23 @[gnome-shell, 2239]: 2462 13:37:03 @[gnome-shell, 1444]: 4 @[gnome-shell, 2239]: 3420 13:37:04 13:37:05 [...] While tracing, this showed that PID 2239 (gnome-shell) and PID 4536 (chrome) suffered over ten thousand swapins. bpftrace-0.23.2/tools/syncsnoop.bt000077500000000000000000000015071477746507000171350ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * syncsnoop Trace sync() variety of syscalls. * For Linux, uses bpftrace and eBPF. * * Also a basic example of bpftrace. * * USAGE: syncsnoop.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing sync syscalls... Hit Ctrl-C to end.\n"); printf("%-9s %-6s %-16s %s\n", "TIME", "PID", "COMM", "EVENT"); } tracepoint:syscalls:sys_enter_sync, tracepoint:syscalls:sys_enter_syncfs, tracepoint:syscalls:sys_enter_fsync, tracepoint:syscalls:sys_enter_fdatasync, tracepoint:syscalls:sys_enter_sync_file_range*, tracepoint:syscalls:sys_enter_msync { time("%H:%M:%S "); printf("%-6d %-16s %s\n", pid, comm, probe); } bpftrace-0.23.2/tools/syncsnoop_example.txt000066400000000000000000000010351477746507000210530ustar00rootroot00000000000000Demonstrations of syncsnoop, the Linux bpftrace/eBPF version. Tracing file system sync events: # ./syncsnoop.bt Attaching 7 probes... Tracing sync syscalls... Hit Ctrl-C to end. TIME PID COMM EVENT 02:02:17 27933 sync tracepoint:syscalls:sys_enter_sync 02:03:43 27936 sync tracepoint:syscalls:sys_enter_sync The output shows calls to the sync() syscall (traced via its tracepoint), along with various details. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.23.2/tools/syscount.bt000077500000000000000000000015501477746507000167670ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * syscount.bt Count system calls. * For Linux, uses bpftrace, eBPF. * * This is a bpftrace version of the bcc tool of the same name. * The bcc versions translates syscall IDs to their names, and this version * currently does not. Syscall IDs can be listed by "ausyscall --dump". * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 13-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Counting syscalls... Hit Ctrl-C to end.\n"); // ausyscall --dump | awk 'NR > 1 { printf("\t@sysname[%d] = \"%s\";\n", $1, $2); }' } tracepoint:raw_syscalls:sys_enter { @syscall[args.id] = count(); @process[comm] = count(); } END { printf("\nTop 10 syscalls IDs:\n"); print(@syscall, 10); clear(@syscall); printf("\nTop 10 processes:\n"); print(@process, 10); clear(@process); } bpftrace-0.23.2/tools/syscount_example.txt000066400000000000000000000021701477746507000207100ustar00rootroot00000000000000Demonstrations of syscount, the Linux bpftrace/eBPF version. syscount counts system calls, and prints summaries of the top ten syscall IDs, and the top ten process names making syscalls. For example: # ./syscount.bt Attaching 3 probes... Counting syscalls... Hit Ctrl-C to end. ^C Top 10 syscalls IDs: @syscall[6]: 36862 @syscall[21]: 42189 @syscall[13]: 44532 @syscall[12]: 58456 @syscall[9]: 82113 @syscall[8]: 95575 @syscall[5]: 147658 @syscall[3]: 163269 @syscall[2]: 270801 @syscall[4]: 326333 Top 10 processes: @process[rm]: 14360 @process[tail]: 16011 @process[objtool]: 20767 @process[fixdep]: 28489 @process[as]: 48982 @process[gcc]: 90652 @process[command-not-fou]: 172874 @process[sh]: 270515 @process[cc1]: 482888 @process[make]: 1404065 The above output was traced during a Linux kernel build, and the process name with the most syscalls was "make" with 1,404,065 syscalls while tracing. The highest syscall ID was 4, which is stat(). There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides different command line options, and translates the syscall IDs to their syscall names. bpftrace-0.23.2/tools/tcpaccept.bt000077500000000000000000000036701477746507000170530ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpaccept.bt Trace TCP accept()s * For Linux, uses bpftrace and eBPF. * * USAGE: tcpaccept.bt * * This is a bpftrace version of the bcc tool of the same name. * * This uses dynamic tracing of the kernel inet_csk_accept() socket function * (from tcp_prot.accept), and will need to be modified to match kernel changes. * 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 /* * With BTF providing types, socket headers are not needed. * We only need to supply the preprocessor defines in this script. * AF_INET/AF_INET6 are part of the stable arch-independent Linux ABI */ #define AF_INET 2 #define AF_INET6 10 #endif BEGIN { printf("Tracing TCP accepts. Hit Ctrl-C to end.\n"); printf("%-8s %-6s %-14s ", "TIME", "PID", "COMM"); printf("%-39s %-5s %-39s %-5s %s\n", "RADDR", "RPORT", "LADDR", "LPORT", "BL"); } kretprobe:inet_csk_accept { $sk = (struct sock *)retval; $inet_family = $sk->__sk_common.skc_family; if ($inet_family == AF_INET || $inet_family == AF_INET6) { // initialize variable type: $daddr = ntop(0); $saddr = ntop(0); if ($inet_family == AF_INET) { $daddr = ntop($sk->__sk_common.skc_daddr); $saddr = ntop($sk->__sk_common.skc_rcv_saddr); } else { $daddr = ntop( $sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); $saddr = ntop( $sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); } $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; $qlen = $sk->sk_ack_backlog; $qmax = $sk->sk_max_ack_backlog; // Destination port is big endian, it must be flipped $dport = bswap($dport); time("%H:%M:%S "); printf("%-6d %-14s ", pid, comm); printf("%-39s %-5d %-39s %-5d ", $daddr, $dport, $saddr, $lport); printf("%d/%d\n", $qlen, $qmax); } } bpftrace-0.23.2/tools/tcpaccept_example.txt000066400000000000000000000025061477746507000207720ustar00rootroot00000000000000Demonstrations of tcpaccept, the Linux bpftrace/eBPF version. This tool traces the kernel function accepting TCP socket connections (eg, a passive connection via accept(); not connect()). Some example output (IP addresses changed to protect the innocent): # ./tcpaccept.bt Tracing tcp accepts. Hit Ctrl-C to end. TIME PID COMM RADDR RPORT LADDR LPORT BL 00:34:19 3949061 nginx 10.228.22.228 44226 10.229.20.169 8088 0/128 00:34:19 3951399 ruby 127.0.0.1 52422 127.0.0.1 8000 0/128 00:34:19 3949062 nginx 10.228.23.128 35408 10.229.20.169 8080 0/128 This output shows three connections, an IPv4 connections to PID 3951399, a "ruby" process listening on port 8000, and one connection to a "nginx" process listening on port 8080. The remote address and port are also printed, and the accept queue current size as well as maximum size are shown. The overhead of this tool should be negligible, since it is only tracing the kernel function performing accept. It is not tracing every packet and then filtering. This tool only traces successful TCP accept()s. Connection attempts to closed ports will not be shown (those can be traced via other functions). There is another version of this tool in bcc: https://github.com/iovisor/bcc USAGE message: # ./tcpaccept.bt bpftrace-0.23.2/tools/tcpconnect.bt000077500000000000000000000035511477746507000172430ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpconnect.bt Trace TCP connect()s. * For Linux, uses bpftrace and eBPF. * * USAGE: tcpconnect.bt * * This is a bpftrace version of the bcc tool of the same name. * * All connection attempts are traced, even if they ultimately fail. * * This uses dynamic tracing of kernel functions, and will need to be updated * to match kernel changes. * * 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 /* * BTF provides the types, we just need to define AF_INET and AF_INET6. * These are Linux ABI defines, and are not architecture-specific. * With BTF, this allows tcpconnect.bt to work without glibc headers: */ #define AF_INET 2 /* IPv4 */ #define AF_INET6 10 /* IPv6 */ #endif BEGIN { printf("Tracing tcp connections. Hit Ctrl-C to end.\n"); printf("%-8s %-8s %-16s ", "TIME", "PID", "COMM"); printf("%-39s %-6s %-39s %-6s\n", "SADDR", "SPORT", "DADDR", "DPORT"); } kprobe:tcp_connect { let $daddr; let $saddr; $sk = ((struct sock *) arg0); $inet_family = $sk->__sk_common.skc_family; if ($inet_family == AF_INET || $inet_family == AF_INET6) { if ($inet_family == AF_INET) { $daddr = ntop($sk->__sk_common.skc_daddr); $saddr = ntop($sk->__sk_common.skc_rcv_saddr); } else { $daddr = ntop($sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); $saddr = ntop($sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); } $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped $dport = bswap($dport); time("%H:%M:%S "); printf("%-8d %-16s ", pid, comm); printf("%-39s %-6d %-39s %-6d\n", $saddr, $lport, $daddr, $dport); } } bpftrace-0.23.2/tools/tcpconnect_example.txt000066400000000000000000000020751477746507000211650ustar00rootroot00000000000000Demonstrations of tcpconnect, the Linux bpftrace/eBPF version. This tool traces the kernel function performing active TCP connections (eg, via a connect() syscall; accept() are passive connections). Some example output (IP addresses changed to protect the innocent): # ./tcpconnect.bt TIME PID COMM SADDR SPORT DADDR DPORT 00:36:45 1798396 agent 127.0.0.1 5001 10.229.20.82 56114 00:36:45 1798396 curl 127.0.0.1 10255 10.229.20.82 56606 00:36:45 3949059 nginx 127.0.0.1 8000 127.0.0.1 37780 This output shows three connections, one from a "agent" process, one from "curl", and one from "nginx". The output details shows the IP version, source address, source socket port, destination address, and destination port. This traces attempted connections: these may have failed. The overhead of this tool should be negligible, since it is only tracing the kernel functions performing connect. It is not tracing every packet and then filtering. USAGE message: # ./tcpconnect.bt bpftrace-0.23.2/tools/tcpdrop.bt000077500000000000000000000052351477746507000165570ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpdrop.bt Trace TCP kernel-dropped packets/segments. * For Linux, uses bpftrace and eBPF. * * USAGE: tcpdrop.bt * * This is a bpftrace version of the bcc tool of the same name. * * This provides information such as packet details, socket state, and kernel * stack trace for packets/segments that were dropped via kfree_skb. * It cannot show tcp flags. * * For Linux 5.17+ (see tools/old for script for lower versions). * * Copyright (c) 2018 Dale Hamel. * Licensed under the Apache License, Version 2.0 (the "License") * * 23-Nov-2018 Dale Hamel created this. * 01-Oct-2022 Rong Tao use tracepoint:skb:kfree_skb */ #ifndef BPFTRACE_HAVE_BTF #include #include #else /* * With BTF providing types, socket headers are not needed. * We only need to supply the preprocessor defines in this script. * AF_INET/AF_INET6 are part of the stable arch-independent Linux ABI */ #define AF_INET 2 #define AF_INET6 10 #endif BEGIN { printf("Tracing tcp drops. Hit Ctrl-C to end.\n"); printf("%-8s %-8s %-16s %-21s %-21s %-8s\n", "TIME", "PID", "COMM", "SADDR:SPORT", "DADDR:DPORT", "STATE"); // See https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h @tcp_states[1] = "ESTABLISHED"; @tcp_states[2] = "SYN_SENT"; @tcp_states[3] = "SYN_RECV"; @tcp_states[4] = "FIN_WAIT1"; @tcp_states[5] = "FIN_WAIT2"; @tcp_states[6] = "TIME_WAIT"; @tcp_states[7] = "CLOSE"; @tcp_states[8] = "CLOSE_WAIT"; @tcp_states[9] = "LAST_ACK"; @tcp_states[10] = "LISTEN"; @tcp_states[11] = "CLOSING"; @tcp_states[12] = "NEW_SYN_RECV"; } tracepoint:skb:kfree_skb { let $daddr; let $saddr; $reason = args.reason; $skb = (struct sk_buff *)args.skbaddr; $sk = ((struct sock *) $skb->sk); $inet_family = $sk->__sk_common.skc_family; if ($reason > SKB_DROP_REASON_NOT_SPECIFIED && ($inet_family == AF_INET || $inet_family == AF_INET6)) { if ($inet_family == AF_INET) { $daddr = ntop($sk->__sk_common.skc_daddr); $saddr = ntop($sk->__sk_common.skc_rcv_saddr); } else { $daddr = ntop($sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); $saddr = ntop($sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); } $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped $dport = bswap($dport); $state = $sk->__sk_common.skc_state; $statestr = @tcp_states[$state]; time("%H:%M:%S "); printf("%-8d %-16s ", pid, comm); printf("%39s:%-6d %39s:%-6d %-10s\n", $saddr, $lport, $daddr, $dport, $statestr); printf("%s\n", kstack); } } END { clear(@tcp_states); } bpftrace-0.23.2/tools/tcpdrop_example.txt000066400000000000000000000023501477746507000204740ustar00rootroot00000000000000Demonstrations of tcpdrop, the Linux bpftrace/eBPF version. tcpdrop prints details of TCP packets or segments that were dropped by the kernel, including the kernel stack trace that led to the drop: # ./tcpdrop.bt TIME PID COMM SADDR:SPORT DADDR:DPORT STATE 00:39:21 0 swapper/2 10.231.244.31:3306 10.229.20.82:50552 ESTABLISHE tcp_drop+0x1 tcp_v4_do_rcv+0x135 tcp_v4_rcv+0x9c7 ip_local_deliver_finish+0x62 ip_local_deliver+0x6f ip_rcv_finish+0x129 ip_rcv+0x28f __netif_receive_skb_core+0x432 __netif_receive_skb+0x18 netif_receive_skb_internal+0x37 napi_gro_receive+0xc5 ena_clean_rx_irq+0x3c3 ena_io_poll+0x33f net_rx_action+0x140 __softirqentry_text_start+0xdf irq_exit+0xb6 do_IRQ+0x82 ret_from_intr+0x0 native_safe_halt+0x6 default_idle+0x20 arch_cpu_idle+0x15 default_idle_call+0x23 do_idle+0x17f cpu_startup_entry+0x73 rest_init+0xae start_kernel+0x4dc x86_64_start_reservations+0x24 x86_64_start_kernel+0x74 secondary_startup_64+0xa5 [...] The last column shows the state of the TCP session. This tool is useful for debugging high rates of drops, which can cause the remote end to do timer-based retransmits, hurting performance. USAGE: # ./tcpdrop.bt bpftrace-0.23.2/tools/tcplife.bt000077500000000000000000000056751477746507000165420ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcplife - Trace TCP session lifespans with connection details. * * See BPF Performance Tools, Chapter 10, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 17-Apr-2019 Brendan Gregg Created this. */ #ifndef BPFTRACE_HAVE_BTF #include #include #include #include #else /* * With BTF providing types, socket headers are not needed. * We only need to supply the preprocessor defines in this script. * AF_INET/AF_INET6 are part of the stable arch-independent Linux ABI */ #define AF_INET 2 #define AF_INET6 10 #endif BEGIN { printf("%-5s %-10s %-15s %-5s %-15s %-5s ", "PID", "COMM", "LADDR", "LPORT", "RADDR", "RPORT"); printf("%5s %5s %s\n", "TX_KB", "RX_KB", "MS"); } kprobe:tcp_set_state { $sk = (struct sock *)arg0; $newstate = arg1; /* * This tool includes PID and comm context. From TCP this is best * effort, and may be wrong in some situations. It does this: * - record timestamp on any state < TCP_FIN_WAIT1 * note some state transitions may not be present via this kprobe * - cache task context on: * TCP_SYN_SENT: tracing from client * TCP_LAST_ACK: client-closed from server * - do output on TCP_CLOSE: * fetch task context if cached, or use current task */ // record first timestamp seen for this socket if ($newstate < TCP_FIN_WAIT1 && @birth[$sk] == 0) { @birth[$sk] = nsecs; } // record PID & comm on SYN_SENT if ($newstate == TCP_SYN_SENT || $newstate == TCP_LAST_ACK) { @skpid[$sk] = pid; @skcomm[$sk] = comm; } // session ended: calculate lifespan and print if ($newstate == TCP_CLOSE && @birth[$sk]) { $delta_ms = (nsecs - @birth[$sk]) / 1e6; $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; $dport = bswap($dport); $tp = (struct tcp_sock *)$sk; $pid = @skpid[$sk]; $comm = @skcomm[$sk]; if ($comm == "") { // not cached, use current task $pid = pid; $comm = comm; } $family = $sk->__sk_common.skc_family; $saddr = ntop(0); $daddr = ntop(0); if ($family == AF_INET) { $saddr = ntop(AF_INET, $sk->__sk_common.skc_rcv_saddr); $daddr = ntop(AF_INET, $sk->__sk_common.skc_daddr); } else { // AF_INET6 $saddr = ntop(AF_INET6, $sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); $daddr = ntop(AF_INET6, $sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); } printf("%-5d %-10.10s %-15s %-5d %-15s %-6d ", $pid, $comm, $saddr, $lport, $daddr, $dport); printf("%5d %5d %d\n", $tp->bytes_acked / 1024, $tp->bytes_received / 1024, $delta_ms); delete(@birth, $sk); delete(@skpid, $sk); delete(@skcomm, $sk); } } END { clear(@birth); clear(@skpid); clear(@skcomm); } bpftrace-0.23.2/tools/tcplife_example.txt000066400000000000000000000030751477746507000204540ustar00rootroot00000000000000Demonstrations of tcplife, the Linux bpftrace/eBPF version. This tool shows the lifespan of TCP sessions, including througphut statistics, and for efficiency only instruments TCP state changes (rather than all packets). For example: # ./tcplife.bt PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS 20976 ssh 127.0.0.1 56766 127.0.0.1 22 6 10584 3059 20977 sshd 127.0.0.1 22 127.0.0.1 56766 10584 6 3059 14519 monitord 127.0.0.1 44832 127.0.0.1 44444 0 0 0 4496 Chrome_IOT 7f00:6:5ea7::a00:0 42846 0:0:bb01:: 443 0 3 12441 4496 Chrome_IOT 7f00:6:5aa7::a00:0 42842 0:0:bb01:: 443 0 3 12436 4496 Chrome_IOT 7f00:6:62a7::a00:0 42850 0:0:bb01:: 443 0 3 12436 4496 Chrome_IOT 7f00:6:5ca7::a00:0 42844 0:0:bb01:: 443 0 3 12442 4496 Chrome_IOT 7f00:6:60a7::a00:0 42848 0:0:bb01:: 443 0 3 12436 4496 Chrome_IOT 10.0.0.65 33342 54.241.2.241 443 0 3 10717 4496 Chrome_IOT 10.0.0.65 33350 54.241.2.241 443 0 3 10711 4496 Chrome_IOT 10.0.0.65 33352 54.241.2.241 443 0 3 10712 14519 monitord 127.0.0.1 44832 127.0.0.1 44444 0 0 0 The output begins with a localhost ssh connection, so both endpoints can be seen: the ssh process (PID 20976) which received 10584 Kbytes, and the sshd process (PID 20977) which transmitted 10584 Kbytes. This session lasted 3059 milliseconds. Other sessions can also be seen, including IPv6 connections. bpftrace-0.23.2/tools/tcpretrans.bt000077500000000000000000000044441477746507000172720ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpretrans.bt Trace or count TCP retransmits * For Linux, uses bpftrace and eBPF. * * USAGE: tcpretrans.bt * * This is a bpftrace version of the bcc tool of the same name. * It doesn't support tracking TLPs. * * This uses dynamic tracing of kernel functions, and will need to be updated * to match kernel changes. * * 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 /* * With BTF providing types, socket headers are not needed. * We only need to supply the preprocessor defines in this script. * AF_INET/AF_INET6 are part of the stable arch-independent Linux ABI */ #define AF_INET 2 #define AF_INET6 10 #endif BEGIN { printf("Tracing tcp retransmits. Hit Ctrl-C to end.\n"); printf("%-8s %-8s %20s %21s %6s\n", "TIME", "PID", "LADDR:LPORT", "RADDR:RPORT", "STATE"); // See include/net/tcp_states.h: @tcp_states[1] = "ESTABLISHED"; @tcp_states[2] = "SYN_SENT"; @tcp_states[3] = "SYN_RECV"; @tcp_states[4] = "FIN_WAIT1"; @tcp_states[5] = "FIN_WAIT2"; @tcp_states[6] = "TIME_WAIT"; @tcp_states[7] = "CLOSE"; @tcp_states[8] = "CLOSE_WAIT"; @tcp_states[9] = "LAST_ACK"; @tcp_states[10] = "LISTEN"; @tcp_states[11] = "CLOSING"; @tcp_states[12] = "NEW_SYN_RECV"; } kprobe:tcp_retransmit_skb { $sk = (struct sock *)arg0; $inet_family = $sk->__sk_common.skc_family; if ($inet_family == AF_INET || $inet_family == AF_INET6) { // initialize variable type: $daddr = ntop(0); $saddr = ntop(0); if ($inet_family == AF_INET) { $daddr = ntop($sk->__sk_common.skc_daddr); $saddr = ntop($sk->__sk_common.skc_rcv_saddr); } else { $daddr = ntop( $sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); $saddr = ntop( $sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); } $lport = $sk->__sk_common.skc_num; $dport = $sk->__sk_common.skc_dport; // Destination port is big endian, it must be flipped $dport = bswap($dport); $state = $sk->__sk_common.skc_state; $statestr = @tcp_states[$state]; time("%H:%M:%S "); printf("%-8d %14s:%-6d %14s:%-6d %6s\n", pid, $saddr, $lport, $daddr, $dport, $statestr); } } END { clear(@tcp_states); } bpftrace-0.23.2/tools/tcpretrans_example.txt000066400000000000000000000022011477746507000212010ustar00rootroot00000000000000Demonstrations of tcpretrans, the Linux bpftrace/eBPF version. This tool traces the kernel TCP retransmit function to show details of these retransmits. For example: # ./tcpretrans.bt 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 connection from 10.153.223.157 port 22 to 69.53.245.40 port 34619. The TCP state was "ESTABLISHED" at the time of the retransmit. The on-CPU PID at the time of the retransmit is printed, in this case 0 (the kernel, which will be the case most of the time). Retransmits are usually a sign of poor network health, and this tool is useful for their investigation. Unlike using tcpdump, this tool has very low overhead, as it only traces the retransmit function. It also prints additional kernel details: the state of the TCP session at the time of the retransmit. USAGE message: # ./tcpretrans.bt bpftrace-0.23.2/tools/tcpsynbl.bt000077500000000000000000000017021477746507000167350ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * tcpsynbl - Show TCP SYN backlog as a histogram. * * See BPF Performance Tools, Chapter 10, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 19-Apr-2019 Brendan Gregg Created this. */ #ifndef BPFTRACE_HAVE_BTF #include #endif BEGIN { printf("Tracing SYN backlog size. Ctrl-C to end.\n"); } kprobe:tcp_v4_syn_recv_sock, kprobe:tcp_v6_syn_recv_sock { $sock = (struct sock *)arg0; @backlog[$sock->sk_max_ack_backlog & 0xffffffff] = hist($sock->sk_ack_backlog); if ($sock->sk_ack_backlog > $sock->sk_max_ack_backlog) { time("%H:%M:%S dropping a SYN.\n"); } } END { printf("\n@backlog[backlog limit]: histogram of backlog size\n"); } bpftrace-0.23.2/tools/tcpsynbl_example.txt000066400000000000000000000016521477746507000206630ustar00rootroot00000000000000Demonstrations of tcpsynbl, the Linux bpftrace/eBPF version. This tool shows the TCP SYN backlog size during SYN arrival as a histogram. This lets you see how close your applications are to hitting the backlog limit and dropping SYNs (causing performance issues with SYN retransmits). For example: # ./tcpsynbl.bt Attaching 4 probes... Tracing SYN backlog size. Ctrl-C to end. ^C @backlog[backlog limit]: histogram of backlog size @backlog[500]: [0] 2266 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [1] 3 | | [2, 4) 1 | | This output shows that for the backlog limit of 500, there were 2266 SYN arrivals where the backlog was zero, three where the backlog was one, and one where the backlog was either two or three. This indicates that we are nowhere near this limit. bpftrace-0.23.2/tools/threadsnoop.bt000077500000000000000000000013601477746507000174250ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * threadsnoop - List new thread creation. * * See BPF Performance Tools, Chapter 13, for an explanation of this tool. * * Copyright (c) 2019 Brendan Gregg. * Licensed under the Apache License, Version 2.0 (the "License"). * This was originally created for the BPF Performance Tools book * published by Addison Wesley. ISBN-13: 9780136554820 * When copying or porting, include this comment. * * 15-Feb-2019 Brendan Gregg Created this. */ config = { missing_probes = "ignore" } BEGIN { printf("%-15s %7s %-16s %s\n", "TIME", "PID", "COMM", "FUNC"); } uprobe:libpthread:pthread_create, uprobe:libc:pthread_create { printf("%15s %7d %-16s %s\n", strftime("%H:%M:%S.%f", nsecs), pid, comm, usym(arg2)); } bpftrace-0.23.2/tools/threadsnoop_example.txt000066400000000000000000000022361477746507000213520ustar00rootroot00000000000000Demonstrations of threadsnoop, the Linux bpftrace/eBPF version. Tracing new threads via phtread_create(): # ./threadsnoop.bt Attaching 2 probes... TIME PID COMM FUNC 10:20:31.938572 28549 dockerd threadentry 10:20:31.939213 28549 dockerd threadentry 10:20:31.939405 28549 dockerd threadentry 10:20:31.940642 28549 dockerd threadentry 10:20:31.949060 28549 dockerd threadentry 10:20:31.958319 28549 dockerd threadentry 10:20:31.939152 28549 dockerd threadentry 10:20:31.950978 28549 dockerd threadentry 10:20:32.013269 28579 docker-containe 0x562f30f2e710 10:20:32.036764 28549 dockerd threadentry 10:20:32.083780 28579 docker-containe 0x562f30f2e710 10:20:32.116738 629 systemd-journal 0x7fb7114955c0 10:20:32.116844 629 systemd-journal 0x7fb7114955c0 [...] The output shows a dockerd process creating several threads with the start routine threadentry(), and docker-containe (truncated) and systemd-journal also starting threads: in their cases, the function had no symbol information available, so their addresses are printed in hex. bpftrace-0.23.2/tools/undump.bt000077500000000000000000000014251477746507000164110ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * undump Trace unix domain socket package receive. * For Linux, uses bpftrace and eBPF. * * Also a basic example of bpftrace. * * This is a bpftrace version of the bcc examples/tracing of the same name. * * USAGE: undump.bt * * Copyright 2022 CESTC, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 22-May-2022 Rong Tao Created this. */ #ifndef BPFTRACE_HAVE_BTF #include #endif BEGIN { printf("Dump UNIX socket packages RX. Ctrl-C to end\n"); printf("%-8s %-16s %-8s %-8s %-s\n", "TIME", "COMM", "PID", "SIZE", "DATA"); } kprobe:unix_stream_read_actor { $skb = (struct sk_buff *)arg0; time("%H:%M:%S "); printf("%-16s %-8d %-8d %r\n", comm, pid, $skb->len, buf($skb->data, $skb->len)); } END { } bpftrace-0.23.2/tools/undump_example.txt000066400000000000000000000012501477746507000203270ustar00rootroot00000000000000Demonstrations of undump.bt, the Linux eBPF/bpftrace version. This example trace the kernel function performing receive AP_UNIX socket packet. Some example output: Terminal 1, UNIX Socket Server: ``` $ nc -lU /var/tmp/unixsocket # receive from Client Hello, world 123abc ``` Terminal 2, UNIX socket Client: ``` $ nc -U /var/tmp/unixsocket # Input some lines Hello, world 123abc ``` Terminal 3, receive tracing: ``` $ sudo ./undump.bt Attaching 3 probes... Dump UNIX socket packages RX. Ctrl-C to end TIME COMM PID SIZE DATA 20:40:11 nc 139071 13 Hello, world\x0a 20:40:14 nc 139071 7 123abc\x0a ^C ``` bpftrace-0.23.2/tools/vfscount.bt000077500000000000000000000010031477746507000167400ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * vfscount Count VFS calls ("vfs_*"). * For Linux, uses bpftrace and eBPF. * * Written as a basic example of counting kernel functions. * * USAGE: vfscount.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing VFS calls... Hit Ctrl-C to end.\n"); } kprobe:vfs_* { @[func] = count(); } bpftrace-0.23.2/tools/vfscount_example.txt000066400000000000000000000022571477746507000206760ustar00rootroot00000000000000Demonstrations of vfscount, the Linux bpftrace/eBPF version. Tracing all VFS calls: # ./vfscount.bt Attaching 54 probes... cannot attach kprobe, Invalid argument Warning: could not attach probe kprobe:vfs_dedupe_get_page.isra.21, skipping. Tracing VFS calls... Hit Ctrl-C to end. ^C @[vfs_fsync_range]: 4 @[vfs_readlink]: 14 @[vfs_statfs]: 56 @[vfs_lock_file]: 60 @[vfs_write]: 276 @[vfs_statx]: 328 @[vfs_statx_fd]: 394 @[vfs_open]: 541 @[vfs_getattr]: 595 @[vfs_getattr_nosec]: 597 @[vfs_read]: 1113 While tracing, the vfs_read() call was the most frequent, occurring 1,113 times. VFS is the Virtual File System: a kernel abstraction for file systems and other resources that expose a file system interface. Much of VFS maps directly to the syscall interface. Tracing VFS calls gives you a high level breakdown of the kernel workload, and starting points for further investigation. 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. There is another version of this tool in bcc: https://github.com/iovisor/bcc bpftrace-0.23.2/tools/vfsstat.bt000077500000000000000000000013211477746507000165660ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * vfsstat Count some VFS calls, with per-second summaries. * For Linux, uses bpftrace and eBPF. * * Written as a basic example of counting multiple events and printing a * per-second summary. * * USAGE: vfsstat.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 06-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing key VFS calls... Hit Ctrl-C to end.\n"); } kprobe:vfs_read*, kprobe:vfs_write*, kprobe:vfs_fsync, kprobe:vfs_open, kprobe:vfs_create { @[func] = count(); } interval:s:1 { time(); print(@); clear(@); } END { clear(@); } bpftrace-0.23.2/tools/vfsstat_example.txt000066400000000000000000000016411477746507000205150ustar00rootroot00000000000000Demonstrations of vfsstat, the Linux bpftrace/eBPF version. This traces some common VFS calls (see the script for the list) and prints per-second summaries. # ./vfsstat.bt Attaching 8 probes... Tracing key VFS calls... Hit Ctrl-C to end. 21:30:38 @[vfs_write]: 1274 @[vfs_open]: 8675 @[vfs_read]: 11515 21:30:39 @[vfs_write]: 1155 @[vfs_open]: 8077 @[vfs_read]: 10398 21:30:40 @[vfs_write]: 1222 @[vfs_open]: 8554 @[vfs_read]: 11011 21:30:41 @[vfs_write]: 1230 @[vfs_open]: 8605 @[vfs_read]: 11077 21:30:42 @[vfs_write]: 1229 @[vfs_open]: 8591 @[vfs_read]: 11061 ^C Each second, a timestamp is printed ("HH:MM:SS") followed by common VFS functions and the number of calls for that second. While tracing, the vfs_read() kernel function was most frequent, occurring over 10,000 times per second. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides command line options. bpftrace-0.23.2/tools/writeback.bt000077500000000000000000000032431477746507000170540ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * writeback Trace file system writeback events with details. * For Linux, uses bpftrace and eBPF. * * This traces when file system dirtied pages are flushed to disk by kernel * writeback, and prints details including when the event occurred, and the * duration of the event. This can be useful for correlating these times with * other performance problems, and if there is a match, it would be a clue * that the problem may be caused by writeback. How quickly the kernel does * writeback can be tuned: see the kernel docs, eg, * vm.dirty_writeback_centisecs. * * USAGE: writeback.bt * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 14-Sep-2018 Brendan Gregg Created this. */ BEGIN { printf("Tracing writeback... Hit Ctrl-C to end.\n"); printf("%-9s %-8s %-8s %-16s %s\n", "TIME", "DEVICE", "PAGES", "REASON", "ms"); // see /sys/kernel/debug/tracing/events/writeback/writeback_start/format @reason[0] = "background"; @reason[1] = "vmscan"; @reason[2] = "sync"; @reason[3] = "periodic"; @reason[4] = "laptop_timer"; @reason[5] = "free_more_memory"; @reason[6] = "fs_free_space"; @reason[7] = "forker_thread"; } tracepoint:writeback:writeback_start { @start[args.sb_dev] = nsecs; } tracepoint:writeback:writeback_written { $sb_dev = args.sb_dev; $s = @start[$sb_dev]; delete(@start, $sb_dev); $lat = $s ? (nsecs - $s) / 1000 : 0; time("%H:%M:%S "); printf("%-8s %-8d %-16s %d.%03d\n", args.name, args.nr_pages & 0xffff, // TODO: explain these bitmasks @reason[args.reason & 0xffffffff], $lat / 1000, $lat % 1000); } END { clear(@reason); clear(@start); } bpftrace-0.23.2/tools/writeback_example.txt000066400000000000000000000036521477746507000210020ustar00rootroot00000000000000Demonstrations of writeback, the Linux bpftrace/eBPF version. This tool traces when the kernel writeback procedure is writing dirtied pages to disk, and shows details such as the time, device numbers, reason for the write back, and the duration. For example: # ./writeback.bt Attaching 4 probes... Tracing writeback... Hit Ctrl-C to end. TIME DEVICE PAGES REASON ms 23:28:47 259:1 15791 periodic 0.005 23:28:48 259:0 15792 periodic 0.004 23:28:52 259:1 15784 periodic 0.003 23:28:53 259:0 18682 periodic 0.003 23:28:55 259:0 41970 background 326.663 23:28:56 259:0 18418 background 332.689 23:28:56 259:0 60402 background 362.446 23:28:57 259:1 18230 periodic 0.005 23:28:57 259:1 65492 background 3.343 23:28:57 259:1 65492 background 0.002 23:28:58 259:0 36850 background 0.000 23:28:58 259:0 13298 background 597.198 23:28:58 259:0 55282 background 322.050 23:28:59 259:0 31730 background 336.031 23:28:59 259:0 8178 background 357.119 23:29:01 259:0 50162 background 1803.146 23:29:02 259:0 27634 background 1311.876 23:29:03 259:0 6130 background 331.599 23:29:03 259:0 50162 background 293.968 23:29:03 259:0 28658 background 284.946 23:29:03 259:0 7154 background 286.572 [...] By looking a the timestamps and latency, it can be seen that the system was not spending much time in writeback until 23:28:55, when "background" writeback began, taking over 300 milliseconds per flush. If timestamps of heavy writeback coincide with times when applications suffered performance issues, that would be a clue that they are correlated and there is contention for the disk devices. There are various ways to tune this: eg, vm.dirty_writeback_centisecs. bpftrace-0.23.2/tools/xfsdist.bt000077500000000000000000000017641477746507000165730ustar00rootroot00000000000000#!/usr/bin/env bpftrace /* * xfsdist Summarize XFS operation latency. * For Linux, uses bpftrace and eBPF. * * This traces four common file system calls: read, write, open, and fsync. * It can be customized to trace more if desired. * * USAGE: xfsdist.bt * * This is a bpftrace version of the bcc tool of the same name. * * Copyright 2018 Netflix, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 08-Sep-2018 Brendan Gregg Created this. */ config = { missing_probes = "ignore" } BEGIN { printf("Tracing XFS operation latency... Hit Ctrl-C to end.\n"); } kprobe:xfs_file_read_iter, kprobe:xfs_file_write_iter, kprobe:xfs_file_open, kprobe:xfs_file_fsync { @start[tid] = nsecs; @name[tid] = func; } kretprobe:xfs_file_read_iter, kretprobe:xfs_file_write_iter, kretprobe:xfs_file_open, kretprobe:xfs_file_fsync /@start[tid]/ { @us[@name[tid]] = hist((nsecs - @start[tid]) / 1000); delete(@start, tid); delete(@name, tid); } END { clear(@start); clear(@name); } bpftrace-0.23.2/tools/xfsdist_example.txt000066400000000000000000000065331477746507000205140ustar00rootroot00000000000000Demonstrations of xfsdist, the Linux bpftrace/eBPF version. xfsdist traces XFS reads, writes, opens, and fsyncs, and summarizes their latency as a power-of-2 histogram. For example: # xfsdist.bt Attaching 9 probes... Tracing XFS operation latency... Hit Ctrl-C to end. ^C @us[xfs_file_write_iter]: [8, 16) 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@ | [16, 32) 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| @us[xfs_file_read_iter]: [1] 724 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [2, 4) 137 |@@@@@@@@@ | [4, 8) 143 |@@@@@@@@@@ | [8, 16) 37 |@@ | [16, 32) 11 | | [32, 64) 22 |@ | [64, 128) 7 | | [128, 256) 0 | | [256, 512) 485 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | [512, 1K) 149 |@@@@@@@@@@ | [1K, 2K) 98 |@@@@@@@ | [2K, 4K) 85 |@@@@@@ | [4K, 8K) 27 |@ | [8K, 16K) 29 |@@ | [16K, 32K) 25 |@ | [32K, 64K) 1 | | [64K, 128K) 0 | | [128K, 256K) 6 | | @us[xfs_file_open]: [1] 1819 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| [2, 4) 272 |@@@@@@@ | [4, 8) 0 | | [8, 16) 9 | | [16, 32) 7 | | This output shows a bi-modal distribution for read latency, with a faster mode of 724 reads that took between 0 and 1 microseconds, and a slower mode of over 485 reads that took between 256 and 512 microseconds. It's likely that the faster mode was a hit from the in-memory file system cache, and the slower mode is a read from a storage device (disk). This "latency" is measured from when the operation was issued from the VFS interface to the file system, to when it completed. This spans everything: block device I/O (disk I/O), file system CPU cycles, file system locks, run queue latency, etc. This is a better measure of the latency suffered by applications reading from the file system than measuring this down at the block device interface. Note that this only traces the common file system operations previously listed: other file system operations (eg, inode operations including getattr()) are not traced. There is another version of this tool in bcc: https://github.com/iovisor/bcc The bcc version provides command line options to customize the output.